BFS

#BFS#

<概念>

寬度優先搜索算法(又稱廣度優先搜索)。BFS,其英文全稱是Breadth First Search。BFS比起DFS更在乎搜索範圍,第一次搜到的一定要是最優解。兩者的實現區別區別主要在於:DFS用的遞歸,而BFS用的隊列輔助。BFS適合與做最優解(最短路,最小方案數)。DFS是一次訪問到底,在逐層返回。而BFS是逐層訪問,一層一層往下,直到找到答案。

隊列:是一種先進先出的數據結構。


BFS要保證一個結點只進入一次隊列才能得到最優解。建立變量,例如vis[i],記錄當前結點是否已經遍歷(入隊)過。

文字基本框架:
1)將s塞入遍歷(s爲所需要放入隊列的結點編號)

2)判斷隊列是否爲空:

   空,結束遍歷;

   含有編號,進行隊頭的拓展,即放入相鄰位置的結點編號,改變隊尾的指針,即讓它後移,並將隊頭指針後移。

3)選擇是否達到最終狀態,是否滿足題意的條件,按照題意進行輸出操作或其他操作。

(僞)代碼基本框架:

……
Int head=1,tail=0;//隊首,隊尾。若head>tail則隊列爲空
……
q[++tail]=S;
vis[S]=true;
while(head<=tail)
{
   int X=q[head++];//queue head取隊首
   For(int i=1;i<=n;++i)//尋找相鄰的結點
     If(adj[x][i]&&!vis[i])//查看是否符合條件:相鄰,且未便利過
{
 q[+tail]=I;
vis[i]=true;
dis[i]=dis[x]+1;//將最新狀態加入隊尾
if(i==T)
 cout<<dis[i]<<endl;//輸出X到T的最少步數
}
}

<例題>

【奇怪的電梯】

-題目描述-

有一天我做了一個夢,夢見了一種很奇怪的電梯。大樓的每一層樓都可以停電梯,而且第i層樓(1<=i<=N)上有一個數字Ki(0<=Ki<=N)。電梯只有四個按鈕:開,關,上,下。上下的層數等於當前樓層上的那個數字。當然,如果不能滿足要求,相應的按鈕就會失靈。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……),從一樓開始。在一樓,按“上”可以到4樓,按“下”是不起作用的,因爲沒有-2樓。那麼,從A樓到B樓至少要按幾次按鈕呢?點擊打開鏈接

-輸入格式-

共二行。第一行爲 3 個用空格隔開的正整數,表示 N,A,B(1≤N≤200, 1≤A,B≤N)N,A,B(1≤N≤200,1≤A,B≤N) 。第二行爲 N 個用空格隔開的非負整數,表示 K_i。

-輸出格式-

一行,即最少按鍵次數,若無法到達,則輸出 -11 。

-樣例數據-

input

5 1 5
3 3 1 2 5

output

3

-分析-

這道題所要求的是最小值(最少要按的按鈕數)。用BFS找到答案便可以退出(DFS不能保證答案爲最優解)。要注意邊界:是否可以繼續往上或向下(沒有0樓,-1樓......)。

-代碼-

#include<bits/stdc++.h>
using namespace std;
int n,a,b;
int que[222]={};
int k[222]={};
int vis[222]={};//標記是否詢問過
int tmp[222]={};
int head=1,tail=0;//隊首隊尾指針
void bfs(int now)
{
	
	for(;head<=tail;)//當隊列不爲空時
	{
		int top=que[head];//top爲隊首的層數
	    head++;//彈出隊首
	    if(top==b)
	     return ;//假如已經到達了目標(b)層,直接退出
	    if(top+k[top]<=n&&vis[top+k[top]]==0)//假如沒有超出邊界並且沒有訪問過
	    {
	    	que[++tail]=top+k[top];//入隊
	    	tmp[top+k[top]]=tmp[top]+1;//次數爲上一次+1
	    	vis[top+k[top]]=1;//標記
	    }
	    if(top-k[top]>=1&&vis[top-k[top]]==0)
	    {
	    	que[++tail]=top-k[top];
	    	tmp[top-k[top]]=tmp[top]+1;
	    	vis[top-k[top]]=1;
	    }
	    
	}
	return ;
}
int main()
{
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++)
	 cin>>k[i];
	que[++tail]=a;
	vis[a]=1;
	bfs(a);
	if(vis[b]==0)//假如沒有訪問過
	 cout<<-1<<endl;
	else//訪問過
	 cout<<tmp[b]<<endl;
	return 0;
}

【青銅蓮花池】

-題目描述-

爲了讓奶牛們娛樂和鍛鍊,農夫約翰建造了一個美麗的池塘。這個長方形的池子被分成了 M 行 N 列個方格(1 ≤ M, N ≤ 30) 。一些格子是堅固得令人驚訝的蓮花,還有一些格子是岩石,其餘的只是美麗、純淨、湛藍的水。貝西正在練習芭蕾舞,她站在一朵蓮花上,想跳到另一朵蓮花上去,她只能從一朵蓮花跳到另一朵蓮花上,既不能跳到水裏,也不能跳到岩石上。貝西的舞步很像象棋中的馬步:每次總是先橫向移動 M1 (1 ≤ M1 ≤ 30)格,再縱向移動 M2 (1 ≤ M2 ≤ 30, M1≠M2)格,或先縱向移動 M1 格,再橫向移動 M2 格。最多時,貝西會有八個移動方向可供選擇。給定池塘的佈局和貝西的跳躍長度,請計算貝西從起點出發,到達目的地的最小步數,我們保證輸入數據中的目的地一定是可達的。點擊打開鏈接

-輸入格式-

第一行:四個用空格分開的整數:M,N,M1 和 M2第二行到 M + 1 行:第 i + 1 行有 N 個用空格分開的整數,描述了池塘第i 行的狀態:0 爲水,1 爲蓮花,2 爲岩石,3 爲貝西所在的起點,4 爲貝西想去的終點。

-輸出格式-

一行,從起點到終點的最少步數。

-樣例數據-

input

4 5 1 2
1 0 1 0 1
3 0 2 0 4
0 1 2 0 0

output

2

-分析-

貝西的跳躍方式像馬(馬按“日”字形走(點擊打開鏈接))。只要列出八個方向,判斷這幾個方向是否可以進行跳躍(跳躍到的位置必須有蓮葉且先前沒有進行過標記),如果可以則入隊,不斷彈出隊首,直至隊列爲空或者跳到目標位置,跳出循環。

-代碼-

#include<bits/stdc++.h>
using namespace std;
int m,n,m1,m2;
int a[33][33]={};
int vis[33][33]={};
int ans[33][33]={};
int lx,ly;
struct kk
{
	int x;
	int y;
} que[3333]={};
int head=1,tail=0;
int bfs()
{
    for(;head<=tail;)
    {
    	int x=que[head].x;
    	int y=que[head].y;//記錄隊首的座標
    	head++;//彈出隊首
    	if(x==lx&&y==ly)
    	 return ans[x][y];//返回答案
	    if(x+m1<=m&&y+m2<=n&&vis[x+m1][y+m2]==0&&a[x+m1][y+m2]==1)
	    {
	    	que[++tail]=(kk){x+m1,y+m2};
	    	vis[x+m1][y+m2]=1;
	    	ans[x+m1][y+m2]=ans[x][y]+1;
		}
	    if(x-m1>0&&y-m2>0&&vis[x-m1][y-m2]==0&&a[x-m1][y-m2]==1)
	    {
	    	que[++tail]=(kk){x-m1,y-m2};
			vis[x-m1][y-m2]=1;
			ans[x-m1][y-m2]=ans[x][y]+1;
		}
	   if(x-m1>0&&y+m2<=n&&vis[x-m1][y+m2]==0&&a[x-m1][y+m2]==1)
	    {
	    	que[++tail]=(kk){x-m1,y+m2};
	    	vis[x-m1][y+m2]=1;
	    	ans[x-m1][y+m2]=ans[x][y]+1;
	   }
	   if(x+m1<=m&&y-m2>0&&vis[x+m1][y-m2]==0&&a[x+m1][y-m2]==1)
        {
        	que[++tail]=(kk){x+m1,y-m2};
        	vis[x+m1][y-m2]=1;
        	ans[x+m1][y-m2]=ans[x][y]+1;
        }
	   if(x+m2<=m&&y+m1<=n&&vis[x+m2][y+m1]==0&&a[x+m2][y+m1]==1)
	   {
	   	    que[++tail]=(kk){x+m2,y+m1};
	   	    vis[x+m2][y+m1]=1;
	   	    ans[x+m2][y+m1]=ans[x][y]+1;
	   	}
	   if(x-m2>0&&y-m1>0&&vis[x-m2][y-m1]==0&&a[x-m2][y-m1]==1)
	   {
	   	    que[++tail]=(kk){x-m2,y-m1};
	   	    vis[x-m2][y-m1]=1;
	   	    ans[x-m2][y-m1]=ans[x][y]+1;
	   	}
	   if(x-m2>0&&y+m1<=n&&vis[x-m2][y+m1]==0&&a[x-m2][y+m1]==1)
	   {
	   	    que[++tail]=(kk){x-m2,y+m1};
	   	    vis[x-m2][y+m1]=1;
	   	    ans[x-m2][y+m1]=ans[x][y]+1;
	   	}
	   if(x+m2<=m&&y-m1>0&&vis[x+m2][y-m1]==0&&a[x+m2][y-m1]==1)
	   {
	   	    que[++tail]=(kk){x+m2,y-m1};
	   	    vis[x+m2][y-m1]=1;
	   	    ans[x+m2][y-m1]=ans[x][y]+1;
	   	}
    }//八個方向進行跳躍
}
int main()
{
	cin>>m>>n>>m1>>m2;
	for(int i=1;i<=m;i++)
	 for(int j=1;j<=n;j++)
	  {
	  	cin>>a[i][j];
	  	if(a[i][j]==3)
	  	{
	  		que[++tail]=(kk){i,j};
			vis[i][j]=1;
	  	}//如果爲初始位置,入隊,並進行標記
		if(a[i][j]==4)
		{
			lx=i;
			ly=j;
			a[i][j]=1;//爲了方便,這樣只需要判斷a[i][j]爲1時可以跳躍
		} //記錄結束位置
	  } 
	cout<<bfs()<<endl;//bfs
	return 0;
}

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