寬度優先搜索BFS,求解迷宮問題
寬度優先搜索(BFS)也是搜索的手段之一。它與深度優先搜索類似,從某個狀態出發搜索所有可達的狀態。
與DFS不同的是搜索的順序,寬度優先搜索總是先搜索離初始狀態近的狀態。也就是說,它是按照開始狀態--->只需1次轉移就可以到達的所有狀態--->只需2次轉移就可以到達的所有狀態--->......,以這樣的順序開始搜索,對於同一個狀態,寬度優先搜索只經過一次,因此時間復雜度:O(狀態數 * 轉移的方式)。
深度優先搜索隱式利用了棧進行計算(遞歸的基礎便是棧),而寬度優先搜索則利用了隊列。搜索時首先將初始狀態添加到隊列裏,此後從隊列的最前端不斷取出狀態,把從該狀態可以轉移到的狀態中尚未訪問過的部分加入隊列,如此往復知道隊列為空或找到問題的解。
隊列的特性為先進先出,那麽我們就可以知道所有的狀態都是按照初始狀態由近及遠的順序被遍歷的。
迷宮問題:
給定一個大小為N×M的迷宮。迷宮由通道和墻壁組成,每一步可以向鄰接的上下左右四格
的通道移動。請求出從起點到終點所需的最小步數。請註意,本題假定從起點一定可以移動
到終點。
輸入
N=10, M=10(迷宮如下圖所示。‘#‘,‘.‘,‘S‘,‘G‘分別表示墻壁、通道、起點和終點)
#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.###.
....#...G#
輸出
22
分析:起點狀態其實就對應初始狀態,起點可走周圍四個方向(過程中需對是否越界,是否撞墻做出判斷,不滿足條件都為不可到達的狀態),一旦遍歷的點為終點這時經歷的路徑必為最短,因為寬度優先搜索是由近到遠的,所以第一個來到終點的路徑必為最短。過程中用二維數組d將到達每個點的最短路徑保存下來。
由於要向四個方向移動,用dx和dy兩個數組來表示四個方向向量。
#include<iostream> #include<cstring> #include<queue> using namespace std; #define INF 0x3f3f3f3f typedef pair<int,int> P; const int MAX = 100; char maze[MAX][MAX+1]; int m,n; int sx,sy;//起點坐標 int gx,gy;//終點坐標 int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1}; intd[MAX][MAX+1]; int bfs(){ queue<P> que; //尋找起點和終點坐標 for(int i=0;i<m;i++){ for(int j=0;j<n;j++){ if(maze[i][j]==‘S‘){ sx=i;sy=j; } if(maze[i][j]==‘G‘){ gx=i;gy=j; } } } memset(d,INF,sizeof(d)); //將初始狀態(起點)加入隊列 que.push(P(sx,sy)); d[sx][sy]=0; while(que.size()){ //取出隊頭 P p = que.front(); que.pop(); int nx=p.first,ny=p.second; if(nx==gx&&ny==gy){ break; } for(int i=0;i<4;i++){ //移動後的位置記為(nx,ny) nx=p.first + dx[i];ny=p.second + dy[i]; if(nx>=0&&ny>=0&&nx<m&&ny<n&&d[nx][ny]==INF&&maze[nx][ny]!=‘#‘){ d[nx][ny]=d[p.first][p.second]+1; que.push(P(nx,ny)); } } } return d[gx][gy]; } int main(){ cin>>m>>n; for(int i=0;i<m;i++){ cin>>maze[i]; } cout<<bfs()<<endl; // cout<<sx<<" "<<sy<<endl; // cout<<gx<<" "<<gy<<endl; return 0; }
寬度優先搜索BFS,求解迷宮問題