題目來源: RevolvingDoors
感覺這道題比前兩道應用BFS演算法的題目要難一些,不像之前的那麼直接,如果這道題目要求的是最短的頻數的話,那跟前兩道就差不多了,但這裡求的是最少旋轉門的次數,這就有點複雜了,我的思路如下:
1. 判斷當前位置是否是終點,不是則搜索由當前位置可以到達的旋轉門,並將旋轉之後的結果狀態存入LD鏈表尾。
2. 從LD鏈表中取出第一個元素作為當前狀態,重複步驟1.
遇到的問題:BFS演算法需要設置一個訪問狀態標誌,要不然會發生重複搜索同一個狀態的情況,開始的時候我只是設置了一個visited數組確定搜索可到達的旋轉門的過程不會出現重複搜索的過程,結果出現了大問題,因為搜索過程中,進入LD中的元素會有重複的,所以會進行大量的重複搜索,時間複雜度大大增加,而且遇到某些地圖還會出現無限循環的情況,如地圖示例1,而地圖示例2直接就是算半天算不出來,後來我增加了一個 isVisited函數確保進入LD的元素跟以前進入過的元素沒有重複,問題才解決了。
還有一個要注意的問題就是地圖示例的情況不是旋轉6次,而是7次,最後一次也要先旋轉門才能到達終點,開始的程序少算了這一步。
代碼如下:
歡迎大家提出改正意見或者在回復裡面show上自己的代碼
/** * TopCoder:RevolvingDoors * (http://community.topcoder.com/stat?c=problem_statement&pm=3064&rd=5869) * Author: xuzhezhao * E-mail: zhezhaoxu@gmail.com * Blog: http://blog.csdn.net/xuzhezhaozhao/ * Date: 2013/6/7 */ #include <iostream> #include <string> #include <list> using namespace std; #define MAX 50 /* 地圖無數限制數 */ #define DOORS_MAX 10 /* 門數限制 */ class RevolvingDoors { public: int turns(string map[]); }; enum Door_State {H, V}; /* 門的狀態,H:水平 V:豎直 */ typedef struct CurrentPos{ int row; /* 當前所處行 */ int col; /* 當前所處列 */ Door_State door[MAX][MAX]; /* 門狀態數組 */ int door_pos[DOORS_MAX]; /* 門位置信息 */ int doors_num; /* 門數量 */ int turn_doors; /* trun的門數量 */ }CurrentPos; static int map_rows = 0; /* 地圖行數 */ static int map_cols = 0; /* 地圖列數 */ static int end_row; /* 終點所在行數 */ static int end_col; /* 終點所在列數 */ static int visited[MAX][MAX]; void init(string map[], CurrentPos &curpos); bool isEnd(CurrentPos curpos); bool isVisited(list <CurrentPos> LV, CurrentPos curpos); int main() { RevolvingDoors revolvingdoors; /* 地圖,以 "" 結尾 */ string map[] = { "#############", "# #|##|# #", "# O O #", "# E || || S #", "# O O #", "# #|##|# #", "#############", "" }; cout << revolvingdoors.turns(map) << endl; return 0; } int RevolvingDoors::turns(string map[]) { list <CurrentPos> LC; /* LC: 內層BFS狀態 */ list <CurrentPos> LD; /* LD: 外層BFS,turn 操作之後的狀態 */ list <CurrentPos> LV; /* LV:外層BFS訪問狀態標誌,類似visited數組,保存turn操作之後的狀態,以避免重複 */ CurrentPos curpos, nextpos; int row, col; init(map, curpos); LD.push_back(curpos); /* 外層BFS */ while (!LD.empty()) { curpos = LD.front(); LD.pop_front(); /* 初始化visited數組 */ for (int i = 0; i < MAX; i++) { for (int j = 0; j < MAX; j++) { visited[i][j] = false; } } LC.push_back(curpos); /* 內層BFS */ while (!LC.empty()) { curpos = LC.front(); LC.pop_front(); row = curpos.row; col = curpos.col; visited[row][col] = true; if (isEnd(curpos)) { return curpos.turn_doors; } /* 可以向上下左右四個方向推進狀態 */ /* 向上走 */ nextpos = curpos; if (row - 1 >= 0 && !visited[row-1][col]) { nextpos.row = row - 1; if (' ' == map[row-1][col]) { LC.push_back(nextpos); } else if ('*' == map[row-1][col]) { /* 進入門的四周區域 */ /* 門的位置有三種情況 */ if (row - 2 >= 0 && 'O' == map[row-2][col] && H == curpos.door[row-2][col] ) { LC.push_back(nextpos); } else if (col + 1 <= map_cols - 1 && 'O' == map[row-1][col+1]) { if (V == curpos.door[row-1][col+1]) { LC.push_back(nextpos); } else { /* turn door */ nextpos.door[row-1][col+1] = V; ++nextpos.turn_doors; visited[row-1][col] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } else if (col - 1 >= 0 && 'O' == map[row-1][col-1]) { if (V == curpos.door[row-1][col-1]) { LC.push_back(nextpos); } else { nextpos.door[row-1][col-1] = V; ++nextpos.turn_doors; visited[row-1][col] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } } } /* 向下走 */ nextpos = curpos; if (row + 1 <= map_rows - 1 && !visited[row+1][col]) { nextpos.row = row + 1; if (' ' == map[row+1][col]) { LC.push_back(nextpos); } else if ('*' == map[row+1][col]) { if (row + 2 <= map_rows - 1 && 'O' == map[row+2][col] && H == curpos.door[row+2][col] ) { LC.push_back(nextpos); } else if (col + 1 <= map_cols - 1 && 'O' == map[row+1][col+1]) { if (V == curpos.door[row+1][col+1]) { LC.push_back(nextpos); } else { /* turn door */ nextpos.door[row+1][col+1] = V; ++nextpos.turn_doors; visited[row+1][col] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } else if (col - 1 >= 0 && 'O' == map[row+1][col-1]) { if (V == curpos.door[row+1][col-1]) { LC.push_back(nextpos); } else { nextpos.door[row+1][col-1] = V; ++nextpos.turn_doors; visited[row+1][col] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } } } /* 向左走 */ nextpos = curpos; if (col - 1 >= 0 && !visited[row][col-1]) { nextpos.col = col - 1; if (' ' == map[row][col-1]) { LC.push_back(nextpos); } else if ('*' == map[row][col-1]) { if (col - 2 >= 0 && 'O' == map[row][col-2] && V == curpos.door[row][col-2] ) { LC.push_back(nextpos); } else if (row - 1 >= 0 && 'O' == map[row-1][col-1]) { if (H == curpos.door[row-1][col-1]) { LC.push_back(nextpos); } else { /* turn door */ nextpos.door[row-1][col-1] = H; ++nextpos.turn_doors; visited[row][col-1] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } else if (row + 1 <= map_rows - 1 && 'O' == map[row+1][col-1]) { if (H == curpos.door[row+1][col-1]) { LC.push_back(nextpos); } else { nextpos.door[row+1][col-1] = H; ++nextpos.turn_doors; visited[row][col-1] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } } } /* 向右走 */ nextpos = curpos; if (col + 1 <= map_cols - 1 && !visited[row][col+1]) { nextpos.col = col + 1; if (' ' == map[row][col+1]) { LC.push_back(nextpos); } else if ('*' == map[row][col+1]) { if (col + 2 <= map_cols - 1 && 'O' == map[row][col+2] && V == curpos.door[row][col+2] ) { LC.push_back(nextpos); } else if (row + 1 <= map_rows - 1 && 'O' == map[row+1][col+1]) { if (H == curpos.door[row+1][col+1]) { LC.push_back(nextpos); } else { /* turn door */ nextpos.door[row+1][col+1] = H; ++nextpos.turn_doors; visited[row][col+1] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } else if (row - 1 >= 0 && 'O' == map[row-1][col+1]) { if (H == curpos.door[row-1][col+1]) { LC.push_back(nextpos); } else { nextpos.door[row-1][col+1] = H; ++nextpos.turn_doors; visited[row][col+1] = true; if (!isVisited(LV, nextpos)){ LV.push_back(nextpos); LD.push_back(nextpos); } } } } } } } return -1; } /** * 初始操作,獲得地圖的基本信息,行,列,門狀態。並將門四周的4個點置為 '*' 字元,將 S, E 置為' '(空格)。 */ void init(string map[], CurrentPos &curpos) { int i, j; curpos.turn_doors = 0; while (map[map_rows] != "") { ++map_rows; } if (map_rows > 0) { map_cols = map[0].length(); } for (i = 0; i < map_rows; i ++) { for (j = 0; j < map_cols; j++) { if ('S' == map[i][j]) { curpos.row = i; curpos.col = j; map[i][j] = ' '; } if ('E' == map[i][j]) { end_row = i; end_col = j; map[i][j] = ' '; } } } curpos.doors_num = 0; for (i = 0; i < map_rows; i ++) { for (j = 0; j < map_cols; j++) { if ('O' == map[i][j]) { curpos.door_pos[curpos.doors_num] = i * map_cols + j; ++curpos.doors_num; if ('|' == map[i+1][j]) { curpos.door[i][j] = V; } else { curpos.door[i][j] = H; } map[i+1][j] = map[i-1][j] = map [i][j+1] = map[i][j-1] = '*'; } } } } /** * 判斷是否到達終點 */ bool isEnd(CurrentPos curpos) { if (curpos.row == end_row && curpos.col == end_col) { return true; } else { return false; } } /** * 外層BFS訪問標誌,若LV中含有curpos狀態,返回true,否則返回false */ bool isVisited(list <CurrentPos> LV, CurrentPos curpos) { list<CurrentPos>::iterator it; int door_row, door_col; bool flag; if (LV.empty()) { return false; } for (it = LV.begin(); it != LV.end(); it++) { if (it->row == curpos.row && it->col == curpos.col) { flag = true; for (int i = 0; i < curpos.doors_num; i++) { door_row = curpos.door_pos[i] / map_cols; door_col = curpos.door_pos[i] % map_cols; if (it->door[door_row][door_col] != curpos.door[door_row][door_col]) { flag = false; break; } } if (flag) { return true; } } } return false; }