挑戰程序設計——迷宮的最短路徑(BFS)

目錄

題解

寬度優先搜索


題目詳情

Description

給定一個大小爲 N * M 的迷宮。迷宮由通道和牆壁組成,每一步可以向鄰接的上下左右四格的通道移動。請求出從起點到終點所需的最小步數

限制條件:

N,M <= 100

Input

輸入兩個數字 N 和 M,分別表示迷宮的長和寬,用空格隔開

輸入代表迷宮的字符串,N 行 M 列,由 '#','~','S','G' 組成,分別表示牆壁,通道,起點,終點

Output

從起點到終點所需的最小步數

Sample Input

10 10
#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#.....
.####.###.
....#...G#

Sample Output

22

題解

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;

const int INF=100000000;
const int MAX_N=100+5,MAX_M=100+5;
typedef pair<int,int> P; //把 pair看作結構體,typedef的用法,P就是該結構體的一個實例。該"結構體"內只有兩個元素 

//輸入
char a[MAX_N][MAX_M]; //表示迷宮的字符串數組 
int N,M; //迷宮的長寬 
int sx,sy; //起點座標 
int gx,gy; //終點座標
int d[MAX_N][MAX_M]; //存放到各個位置的最短距離的數組

//向四個方向移動的向量:右、上、左、下 
int dx[4]={1,0,-1,0}; //dx[i]是向量的橫座標
int dy[4]={0,1,0,-1}; //dy[i]是向量的縱座標

//從(sx,sy)到(gx,gy)的最短距離。如果無法到達,則是INF
int bfs(){
	queue<P> que;
	
	for(int i=0;i<N;++i){
		for(int j=0;j<M;++j){
			d[i][j]=INF; //把所有位置的距離初始化爲INF 
		}
	}
	//將起點加入隊列,並把這一地點的距離設置爲0
	que.push(P(sx,sy));
	d[sx][sy]=0;
	
	//不斷循環直到隊列爲空
	while(que.size()){
		//從隊列最前端取出元素 
		P p=que.front();
		que.pop();
		//如果取出的狀態已經是終點,結束搜索
		if(p.first==gx && p.second==gy){
			break;
		} 
		
		//四個方向的循環 
		for(int i=0;i<4;++i){
			//移動後的位置記爲 (nx,ny)
			int nx=p.first+dx[i],ny=p.second+dy[i];
			//判斷是否可以移動,是否已經訪問過該點(d[nx][ny]!=INF即爲訪問過)
			if(0<=nx && nx<N && 0<=ny && ny<M && a[nx][ny]!='#' && d[nx][ny]==INF){
				//如果能夠移動則加入隊列,且到該位置的距離變成到 p 的距離 +1 
				que.push(P(nx,ny));
				d[nx][ny]=d[p.first][p.second]+1; 
			} 
		} 
	}
	return d[gx][gy];
}
 
int main(){
	scanf("%d%d",&N,&M);
	for(int i=0;i<N;++i){
		scanf("%s",a[i]);
	}
	for(int i=0;i<N;++i){
		for(int j=0;j<M;++j){
			if(a[i][j]=='S'){
				sx=i;
				sy=j;
			}
			if(a[i][j]=='G'){
				gx=i;
				gy=j;
			}
		}
	}
	int res=bfs();
	printf("%d",res);
	return 0;
}

本題中,狀態是目前所在位置的座標,可以構造成 pair 或者編碼成 int 來表達狀態。當狀態更加複雜時,需要封裝成一個類來表示

寬度優先搜索中,只要將已經訪問過的狀態用標記管理起來,就可以很好的做到由近及遠的搜索。本題要求最短距離,可用 d[N][M] 數組保存。初始化時用充分大的常數 INF 初始化它,這樣尚未到達的位置就是 INF,同時起到標記的作用

到達終點時就會停止搜索,如果繼續搜索下去直到隊列爲空,可以計算出到各個位置的最短距離。如果搜索到最後,d 依然是 INF,這個位置就無法從起點到達


寬度優先搜索

寬度優先搜索可以用來求最短路徑,最少操作之類的問題

對於同一個狀態,寬度優先搜索只經過一次,因此時間複雜度:O(狀態數 * 轉移的方式)

寬度優先搜索與深度優先搜索類似,從某個狀態出發,探索所有可以到達的狀態。不同之處在於,寬度優先搜索總是先搜索距離初始狀態近的狀態

深度優先搜索利用棧進行計算,寬度優先搜索則利用了隊列搜索時首先將初始狀態加到隊列裏,此後從隊列的最前端不斷取出狀態,把從該狀態可以轉移到的狀態中尚未訪問過的部分加入隊列,如此往復,直至隊列爲空或找到了問題的解

寬度優先搜索會把狀態逐個加入隊列,通常需要與狀態數成正比的內存空間。反之,深度優先搜索與最大的遞歸深度成正比。一般與狀態數相比,遞歸的深度不會太大,所以可以認爲深度優先搜索更加節省內存

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章