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