深度優先遍歷的棧實現

——PickingupJewels

撿珠寶是典型的需要回溯的深度優先遍歷,它要求找出能獲得最多珠寶的路徑,並且將該路徑輸出。

         這個題比較難的兩點是怎麼不走環路和怎麼回溯。回溯相對簡單一點,就是出棧以後,你要將它置爲未訪問過,不用擔心重複走它,因爲還有方向控制前進的方向。

而對於環路,一開始想得很苦惱,沒明白,多設了很多條件,後來還是在老大的幫助下,想通了其實不重複進棧就不會走環路,因爲棧內的點是剛走過的路。

void DFS(Node* start, Node* end) {
	int dx,dy;
	int i;
	Node* n = start;
	n->dir = 0;  // 入棧則將方向置爲0
	push(start);  // 將起始點入棧
	jewels_num = 0; // 珠寶數
	Node* tmp;  // 取棧頂元素
	if(start->value == 2) jewels_num++;   //如果起點是珠寶,則珠寶數++
	if(end->value == 2) jewels_num++;   //如果終點是珠寶,則珠寶數++
	while(!isEmpty()) {   // 如果棧不爲空
		tmp = getTop();   // 取棧頂節點
		for(i=tmp->dir; i<4; i++) {   // 將方向置爲棧頂元素的方向
			tmp->dir++;  // 棧頂元素方向++,表明走過一個方向(進棧爲0,當dir爲4時則表明該點4個方向已遍歷)
			dx = tmp->x + DIR[i][0];
			dy = tmp->y + DIR[i][1];
			if(dx == end->x && dy == end->y) {  // 如找到終點
				if(jewels_num>Answer) {   // 判斷當前路徑找的珠寶數是否比已保存的數要多
					Answer =  jewels_num;  //  始終保存最大的珠寶數
					saveCurrentPath();  // 並存儲該路徑
				}
#ifdef TEST
				cout<<"find end! jewels_num is "<<jewels_num<<endl;	
#endif
				continue;  // 終點不入棧,其實入棧也行,反正馬上就要出棧
			}
			// 1爲牆,0爲路,2爲珠寶。判斷map[dx][dy].dir == -1則可保證棧內的元素不會被重複入棧,即不走環路
			if(dx>=0 && dx<N && dy>=0 && dy<N && map[dx][dy].value != 1 && map[dx][dy].dir == -1) {
				n = &map[dx][dy];  // 指針指向當前判斷點
				if(n->value == 2) jewels_num++;   // 如果是珠寶,則珠寶數++
				n->dir = 0;  // 將方向置爲0
				push(n);  // 進棧
				break;  // 不再遍歷當前節點
			}
		}
		if(i==4) {  // 當前節點4個方向都走完了
			n = pop();  // 當前節點出棧
			n->dir = -1;  // 並且標記爲未訪問過,回溯
			if(n->value == 2) jewels_num--;   // 如果當前節點爲珠寶,則珠寶數--
		}
	}
}

行走的路線是:

1

5

0 0 0 2 0

2 1 0 1 2

0 0 2 2 0

0 1 0 1 2

2 0 0 0 0

-----------------N=5

3 3 3 3 3

2 1 0 1 3

0 0 2 2 3

0 1 0 1 3

2 0 0 0 3

find end! jewels_num is 3

-----------------N=5

3 3 3 3 3

2 1 0 1 3

0 0 3 3 3

0 1 3 1 2

2 0 3 3 3

find end! jewels_num is 4

-----------------N=5

3 3 3 3 3

2 1 0 1 3

3 3 3 3 3

3 1 0 1 2

3 3 3 3 3

find end! jewels_num is 5

find end! jewels_num is 3

find end! jewels_num is 1

find end! jewels_num is 2

find end! jewels_num is 4

find end! jewels_num is 2

find end! jewels_num is 5

find end! jewels_num is 2

find end! jewels_num is 5

…(find way, but not saved)

-----------------N=5

3 0 3 3 3

3 1 3 1 3

3 0 3 2 3

3 1 3 1 3

3 3 3 0 3

find end! jewels_num is 6

所以,最後輸出的是

Case #1

3 0 3 3 3

3 1 3 1 3

3 0 3 2 3

3 1 3 1 3

3 3 3 0 3

6

本題中棧是用指針來實現的

typedef struct {
	int x;
	int y;
	int value;
	int dir;
}Node;
Node map[MAX][MAX];
int resultMap[MAX][MAX];

typedef struct{
	Node* data[SLENGTH];
	int top;
}Stack;
Stack s;

棧的方法主要有棧空、棧滿、入棧、出棧、取棧頂節點、初始化

就不分開說了,貼個全的代碼吧。


#include <iostream>

using namespace std;
//#define TEST
//#define S_LOG
#define MAX 12
#define SLENGTH 1000
int DIR[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int N;
int Answer;
int jewels_num;
int path_n = 3;

typedef struct {
	int x;
	int y;
	int value;
	int dir;
}Node;
Node map[MAX][MAX];
int resultMap[MAX][MAX];

typedef struct{
	Node* data[SLENGTH];
	int top;
}Stack;
Stack s;

bool isEmpty(){
	if(s.top == 0) {
		return true;
	} else {
		return false;
	}
}

bool isFull(){
	if(s.top >= SLENGTH) {
		return true;
	} else {
		return false;
	}
}

void push(Node* n) {
#ifdef S_LOG
	cout<<"push "<<n->x<<","<<n->y<<endl;				
#endif
	if(!isFull()) {
		s.data[++s.top] = n;
	} else {
#ifdef S_LOG
		cout<<"Stack is full"<<endl;
#endif
	}
}

Node* getTop() {
	Node* n = s.data[s.top];
	return n;
}

Node* pop() {
	Node* n = s.data[s.top];
	if(!isEmpty()) {
		s.top--;
#ifdef S_LOG
	cout<<"pop "<<n->x<<","<<n->y<<endl;				
#endif
	} else {
#ifdef S_LOG
		cout<<"Stack is Empty"<<endl;
#endif
	}
	return n;
}

void init() {
	s.top = 0;
}

void printMap() {
	int i,j;
	for(i=0; i<N; i++) {
		for(j=0; j<N; j++) {
			cout<<map[i][j].value<<" ";
		}
		cout<<endl;
	}
}

void printRMap() {
	int i,j;
	for(i=0; i<N; i++) {
		for(j=0; j<N; j++) {
			cout<<resultMap[i][j]<<" ";
		}
		cout<<endl;
	}
}

// 構建用於輸出的數組。
void resetMap() {
	int i,j;
	for(i=0; i<N; i++) {
		for(j=0; j<N; j++) {
			if(resultMap[i][j] == path_n) {
				resultMap[i][j] = 3;
			} else {
				resultMap[i][j] = map[i][j].value;
			}
		}
	}
	resultMap[N-1][N-1] = 3;
	printRMap();
}

// 存儲當前路徑的方法
void saveCurrentPath(){	
	path_n++;
	int stop = s.top;
	int dx,dy;
	while(stop>0) {
		dx = s.data[stop]->x;
		dy = s.data[stop]->y;
		resultMap[dx][dy] = path_n;
		stop--;
	}
	resetMap();  // 由於resultMap一直都在賦值,因此需要保證它除了本路徑以外沒有走過的痕跡,因此要reset。
}

void DFS(Node* start, Node* end) {
	int dx,dy;
	int i;
	Node* n = start;
	n->dir = 0;  // 入棧則將方向置爲0
	push(start);  // 將起始點入棧
	jewels_num = 0; // 珠寶數
	Node* tmp;  // 取棧頂元素
	if(start->value == 2) jewels_num++;   //如果起點是珠寶,則珠寶數++
	if(end->value == 2) jewels_num++;   //如果終點是珠寶,則珠寶數++
	while(!isEmpty()) {   // 如果棧不爲空
		tmp = getTop();   // 取棧頂節點
		for(i=tmp->dir; i<4; i++) {   // 將方向置爲棧頂元素的方向
			tmp->dir++;  // 棧頂元素方向++,表明走過一個方向(進棧爲0,當dir爲4時則表明該點4個方向已遍歷)
			dx = tmp->x + DIR[i][0];
			dy = tmp->y + DIR[i][1];
			if(dx == end->x && dy == end->y) {  // 如找到終點
				if(jewels_num>Answer) {   // 判斷當前路徑找的珠寶數是否比已保存的數要多
					Answer =  jewels_num;  //  始終保存最大的珠寶數
					saveCurrentPath();  // 並存儲該路徑
				}
#ifdef TEST
				cout<<"find end! jewels_num is "<<jewels_num<<endl;	
#endif
				continue;  // 終點不入棧,其實入棧也行,反正馬上就要出棧
			}
			// 1爲牆,0爲路,2爲珠寶。判斷map[dx][dy].dir == -1則可保證棧內的元素不會被重複入棧,即不走環路
			if(dx>=0 && dx<N && dy>=0 && dy<N && map[dx][dy].value != 1 && map[dx][dy].dir == -1) {
				n = &map[dx][dy];  // 指針指向當前判斷點
				if(n->value == 2) jewels_num++;   // 如果是珠寶,則珠寶數++
				n->dir = 0;  // 將方向置爲0
				push(n);  // 進棧
				break;  // 不再遍歷當前節點
			}
		}
		if(i==4) {  // 當前節點4個方向都走完了
			n = pop();  // 當前節點出棧
			n->dir = -1;  // 並且標記爲未訪問過,回溯
			if(n->value == 2) jewels_num--;   // 如果當前節點爲珠寶,則珠寶數--
		}
	}
}

void clearMap() {
	int i,j;
	for(i=0; i<N; i++) {
		for(j=0; j<N; j++) {
			resultMap[i][j] = 0;
			map[i][j].dir = -1;
			map[i][j].value = 0;
		}
	}
}

int main(int argc, char** argv)
{
	int T, test_case;
	int i,j;

	freopen("input.txt", "r", stdin);

	cin >> T;
	for(test_case = 0; test_case  < T; test_case++)
	{
		cin>>N;
		init();
		Answer = 0;
		for(i=0; i<N; i++) {
			for(j=0; j<N; j++) {
				map[i][j].x = i;
				map[i][j].y = j;
				cin>>map[i][j].value;
				resultMap[i][j] = map[i][j].value;
				map[i][j].dir = -1;
			}
		}
#ifdef TEST
		printMap();
#endif
		DFS(&map[0][0],&map[N-1][N-1]);
		// Print the answer to standard output(screen).
		cout << "Case #" << test_case+1 << endl;
		printRMap();
		cout << Answer << endl;
		clearMap();
	}

	return 0;//Your program should return 0 on normal termination.
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章