迷宮問題(BFS)
阿新 • • 發佈:2018-12-02
給定一個大小為N*M的迷宮,由通道('.')和牆壁('#')組成,其中通道S表示起點,通道G表示終點,每一步移動可以達到上下左右中不是牆壁的位置。試求出起點到終點的最小步數。(本題假定迷宮是有解的)(N,M<=100)
樣例輸入:
10 10
#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
########.#
....#.....
.####.###.
....#...G#
這道題目以及解法均來自《挑戰程式設計競賽(第2版)》第34頁-36頁;書中解法使用了真正的佇列,而我在這裡是用了陣列模擬佇列,但是結果都是一樣的。
個人覺得這個例題很好地表現了廣度優先搜尋是如何與佇列先進先出(FIFO)的思想聯絡起來的,通過不斷取得某個狀態後能夠達到的所有狀態並將其加入佇列, 並且由於佇列本身的特性先加入佇列的狀態總是先得到處理,這樣就達到了一個目的:總是先將需要轉移次數更少的狀態進行分析處理,換句話說就是總是取得了這個狀態的樹中更接近根部的節點,又或者是總是讓搜尋樹的廣度得到儘可能增加。
在這個問題中,找到從起點到終點的最短路徑其實就是一個建立佇列的過程:
1.從起點開始,先將其加入佇列,設定距離為0;
2.從佇列首端取出位置,將從這個位置能夠到達的位置加入佇列,並且讓這些位置的距離為上一個位置的距離加上1;
3.迴圈2直到將終點新增到佇列中,這說明我們已經找到了路徑;
注意到在這個過程中,每次處理的位置所對應的距離是嚴格遞增的,因此一旦找到終點,當時的距離就是最短距離;
同樣基於這個原因,搜尋可移動到的位置所使用的判斷條件中不僅僅是不碰牆壁、不超過邊界,還有一個就是沒有到達過,因為如果已經到達了這個位置,這說明已經有更短的路徑到達這個位置,這次到達這個位置的路徑是更差的,不可能得到更好的最終解。
#include<iostream> #include<cstdio> using namespace std; const int INF = 100000000; char map[3000][3000]; int d[3000][3000]; int mov[4][2] = {1,0,-1,0,0,1,0,-1}; int n,m,sx,sy,gx,gy; pair <int,int> que[1010]; int bfs() { int frt = 0,til = 0; for(int i = 0;i < n; i++) { for(int j = 0;j < m; j++) { d[i][j] = INF; } } que[til].first = sx; que[til++].second = sy; d[sx][sy] = 0; while(frt != til) { int nx = que[frt].first; int ny = que[frt++].second; if(nx == gx && ny == gy) break; for(int i = 0;i < 4; i++) { int x = nx + mov[i][0]; int y = ny + mov[i][1]; if(x >= 0 && x < n && y >= 0 && y < m && map[x][y] != '#' && d[x][y] == INF) { que[til].first = x; que[til++].second = y; d[x][y] = d[nx][ny] + 1; } } } return d[gx][gy]; } int main() { scanf("%d %d",&n,&m); getchar(); for(int i = 0;i < n; i++) { scanf("%s",map[i]); getchar(); for(int j = 0;j < m; j++) { if(map[i][j] == 'S') { sx = i; sy = j; continue; } if(map[i][j] == 'G') { gx = i; gy = j; } } } int res = bfs(); res != INF ? printf("%d",res) : printf("-1"); return 0; }