深度优先遍历的栈实现

——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.
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值