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