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。

-輸出格式-

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

-樣例資料-

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;
}