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