BFS-迷宮問題-用寬度(廣度)優先搜索解決最優路徑問題


題目:
給定一個大小為 N×M 的迷宮。迷宮由通道和牆壁組成,每一步可以向鄰接的上下左右四格
的通道移動。請求出從起點到終點所需的最小步數。請注意,本題假定從起點一定可以移動
到終點。
限制條件;N, M ≤ 100

測試樣例:
N=10, M=10(迷宮如下圖所示。 '#', '.', 'S', 'G'分別表示牆壁、通道、起點和終點)

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

輸出結果:
22

關於深度優先搜索我在之前的博客中有提及:深度優先搜索初嘗試
以及小z的房子

那么這道題目能不能用深度優先搜索解決呢?
很遺憾,深度優先搜索做不到。

在經過三次嘗試以后,得出了DFS解此題的幾大缺陷

  • 1.深度優先搜索,顧名思義,對一條可能的路徑搜索至無法在搜索,再對下一條路徑進行搜索。那么當利用棧來進行DFS搜索,記錄最短路徑的步數,怎么實現?就算你把已經搜索過的位置標記,再利用標記來減去多余的步數,實現的語句也是非常亘長和麻煩的。
  • 2.那么我用隊列來存儲可以嗎?在第一點,大部分實現DFS算法都是利用棧,那么也帶來了一定的麻煩,你經過的每一點都被壓入棧中,並且因為棧的后進先出(LIFO),導致每一次搜索分支結束以后,返回的時候(如果沒有找到終點),都需要退棧。如果使用隊列的話就不會這么麻煩。
    那么用隊列實現的“特別的”DFS算法,能不能行的通?答案也是否定的,原因也是因為記錄最短路徑的步數問題,每次壓入隊列都有多個坐標。讀者可以自己畫一個隊列進行模擬。
  • 3.DFS搜索到的路徑未必是最短的。如果有兩條路徑可以抵達終點,那么DFS最初所選擇的路徑未必是最短的。

用BFS(queue)來實現:
代碼:

#include<cstdio>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<queue>
#define INF 1000;//定義INF; 
using namespace std;
struct p
{
	int x,y;
};//存儲坐標 

int k;
int n,m;

int change[6][3]={{1,0},{-1,0},{0,1},{0,-1}};
char pla[105][105];
int dpla[105][105];//存儲每一點距離的數組 

void BFS(int beginx,int beginy)
{
	int i,j;
	queue<p>q;//存儲坐標的隊列 
	
	p begin,between;
	begin.x=beginx;
	begin.y=beginy;
	
	q.push(begin);
	 
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			dpla[i][j]=INF;//所有位置的距離初始化為INF; 
		} 
	}
	
	dpla[begin.x][begin.y]=0;//開始的地方設置為0;
	pla[begin.x][begin.y]='z';//用'z'替換掉原來的‘.’防止走回去 
	
	while(!q.empty())
	{	
		begin=q.front();//取出隊首開始搜索 
		q.pop();
		if(pla[begin.x][begin.y]=='g')break;//遇到終點break; 
		
		for(i=0;i<4;i++)
		{
			between.x=begin.x+change[i][0];
			between.y=begin.y+change[i][1];//上下左右搜索 
			
			if(between.x>0 && between.x<=n && between.y>0 && between.y<=m)
			{
				if(pla[between.x][between.y]=='.')
				{
					q.push(between);//入列 
					//在dpla記錄每一個坐標的距離 
					dpla[between.x][between.y]=dpla[begin.x][begin.y]+1;
					
					pla[between.x][between.y]='z';//字符替換,聲明已經走過 
				}
				if(pla[between.x][between.y]=='G')//遇到終點 
				{
					q.push(between);//終點入列;
					//記錄至終點的距離 
					dpla[between.x][between.y]=dpla[begin.x][begin.y]+1;
					
					pla[between.x][between.y]='g';//用g替換G,代表搜索到終點; 
				}
			}
		}
		
	} 
	
}

int main()
{
	int i,j;
	scanf("%d%d",&n,&m);
	
	getchar();//回車 
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			scanf("%c",&pla[i][j]);
		}
		getchar();
    }
    
    for(i=1;i<=n;i++)
    {
    	for(j=1;j<=m;j++)
    	{
    		if(pla[i][j]=='S')//找到出發點; 
    		BFS(i,j);
    	}
    }
    
    //下面注釋的代碼判斷距離有沒有出錯 
    /*for(i=1;i<=n;i++)
    {
    	for(j=1;j<=m;j++)
    	{
    		printf("%d ",dpla[i][j]);
    	}
    	printf("\n");
    }*/ 
    
    for(i=1;i<=n;i++)
    {
    	for(j=1;j<=m;j++)
    	{
    		if(pla[i][j]=='g')
    		printf("%d",dpla[i][j]);//輸出終點記錄的距離; 
    	}
    }
    
    return 0;
}

主要注意的地方和變量的名稱我在代碼中作了注釋。寫起來其實和DFS差不多,一個明顯不同的地方就是用了一個數組dpla存儲每一個坐標與原點坐標的距離。
我在敲這段程序的時候,犯了一個錯誤:忘記對搜索過的區域.進行字符的替換,這樣會導致循環無法結束。

昭錫對BFS也有作一篇文章:迷宮問題
關於DFS和BFS深入的理解及其他:BFS和DFS算法介紹與實現 作者:yousheng324。

2016/3/16


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM