雙指標,BFS和圖論(二)
(一)BFS
1.地牢大師
你現在被困在一個三維地牢中,需要找到最快脫離的出路!
地牢由若干個單位立方體組成,其中部分不含岩石障礙可以直接通過,部分包含岩石障礙無法通過。
向北,向南,向東,向西,向上或向下移動一個單元距離均需要一分鐘。
你不能沿對角線移動,迷宮邊界都是堅硬的岩石,你不能走出邊界範圍。
請問,你有可能逃脫嗎?
如果可以,需要多長時間?
輸入格式
輸入包含多組測試資料。
每組資料第一行包含三個整數 L,R,C 分別表示地牢層數,以及每一層地牢的行數和列數。
接下來是 L 個 R 行 C 列的字元矩陣,用來表示每一層地牢的具體狀況。
每個字元用來描述一個地牢單元的具體狀況。
其中, 充滿岩石障礙的單元格用”#”表示,不含障礙的空單元格用”.”表示,你的起始位置用”S”表示,終點用”E”表示。
每一個字元矩陣後面都會包含一個空行。
當輸入一行為”0 0 0”時,表示輸入終止。
輸出格式
每組資料輸出一個結果,每個結果佔一行。
如果能夠逃脫地牢,則輸出”Escaped in x minute(s).”,其中X為逃脫所需最短時間。
如果不能逃脫地牢,則輸出”Trapped!”。
資料範圍
1≤L,R,C≤100
輸入樣例:
3 4 5
S....
.###.
.##..
###.#
#####
#####
##.##
##...
#####
#####
#.###
####E
1 3 3
S##
#E#
###
0 0 0
輸出樣例:
Escaped in 11 minute(s).
Trapped!
解題思路:一道三維的BFS搜尋題,我們可以建立三個移動陣列:vx,vy,vk,分別表示北,南,東,西,上,下,設定一個三維的map陣列來儲存地圖,
設定一個vis陣列,用來判斷是否走過以及距離。
程式碼:
#include<iostream> #include<queue> #include<cstring> using namespace std; const int N=110; int l,r,c; char map[N][N][N]; int vis[N][N][N]; int vx[]={1,-1,0,0,0,0}; int vy[]={0,0,1,-1,0,0}; int vk[]={0,0,0,0,1,-1}; typedef struct Node { int k,x,y; }; bool check(int K,int X,int Y) { if(X<0||X>=r||Y<0||Y>=c||K<0||K>=l) return false; if(map[K][X][Y]=='#') return false; if(vis[K][X][Y]!=0) return false; return true; } int bfs(Node start) { queue<Node> q; memset(vis,0,sizeof(vis)); q.push(start); while(!q.empty()) { Node tem=q.front(); if(map[tem.k][tem.x][tem.y]=='E') return vis[tem.k][tem.x][tem.y]; q.pop(); for(int i=0;i<6;i++) { int X=tem.x+vx[i]; int Y=tem.y+vy[i]; int K=tem.k+vk[i]; if(check(K,X,Y)==false) continue; vis[K][X][Y]=vis[tem.k][tem.x][tem.y]+1; Node t={K,X,Y}; q.push(t); } } return 0; } int main() { int i,j,bx,by,bk,k; Node start; string ss; while(1) { cin>>l>>r>>c; if(l==0&&r==0&&c==0) break; for(k=0;k<l;k++) { for(i=0;i<r;i++) { for(j=0;j<c;j++) { cin>>map[k][i][j]; if(map[k][i][j]=='S') { bk=k,bx=i,by=j; start={bk,bx,by}; } } } getline(cin,ss); } int ans=bfs(start); if(ans) cout<<"Escaped in "<<ans<<" minute(s)."<<endl; else cout<<"Trapped!"<<endl; } return 0; }
2.全球變暖
你有一張某海域 N×N 畫素的照片,”.”表示海洋、”#”表示陸地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中”上下左右”四個方向上連在一起的一片陸地組成一座島嶼,例如上圖就有 2 座島嶼。
由於全球變暖導致了海面上升,科學家預測未來幾十年,島嶼邊緣一個畫素的範圍會被海水淹沒。
具體來說如果一塊陸地畫素與海洋相鄰(上下左右四個相鄰畫素中有海洋),它就會被淹沒。
例如上圖中的海域未來會變成如下樣子:
.......
.......
.......
.......
....#..
.......
.......
請你計算:依照科學家的預測,照片中有多少島嶼會被完全淹沒。
輸入格式
第一行包含一個整數N。
以下 N 行 N 列,包含一個由字元”#”和”.”構成的 N×N 字元矩陣,代表一張海域照片,”#”表示陸地,”.”表示海洋。
照片保證第 1 行、第 1 列、第 N 行、第 N 列的畫素都是海洋。
輸出格式
一個整數表示答案。
資料範圍
1≤N≤1000
輸入樣例1:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
輸出樣例1:
1
輸入樣例2:
9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........
輸出樣例2:
1
解題思路:該題要找出完全被淹沒的島嶼的個數,首先我們要做的是找出所有的聯通快,我們可以用bfs來找,對於每一個聯通快我們需要判斷他是否被完全淹沒,如何判斷呢?
我們可以找出該聯通塊裡一共有多少個畫素,再找出有多少個畫素與海相鄰,如果兩者的個數相等,那麼該島嶼必然會被完全淹沒。
否則,不會完全淹沒。特別注意:當你用bfs開始尋找時,此時的畫素總數的初始值為1.
程式碼:
#include<iostream> #include<cstring> #include<queue> #include<cstdio> using namespace std; const int N=1010; char map[N][N]; bool ts[N][N]; int vx[]={1,-1,0,0}; int vy[]={0,0,1,-1}; int ans,n; typedef struct Node { int x,y; }; bool check(int X,int Y) { if(X<0||X>=n||Y<0||Y>=n) return false; if(map[X][Y]=='.') return false; if(ts[X][Y]==true) return false; return true; } void bfs(int i,int j) { Node start={i,j}; queue<Node> q; ts[i][j]=true; q.push(start); int total=1,ver=0; while(q.size()) { Node t=q.front(); q.pop(); int flag=false; for(i=0;i<4;i++) { int X=t.x+vx[i]; int Y=t.y+vy[i]; if(X>=0&&X<n&&Y>=0&&Y<n&&map[X][Y]=='.') { flag=true; } if(check(X,Y)==false) continue; total++; ts[X][Y]=true; Node f={X,Y}; q.push(f); } if(flag) ver++; } if(total==ver) ans++; } int main() { int i,j; cin>>n; for(i=0;i<n;i++) { for(j=0;j<n;j++) { cin>>map[i][j]; } } for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(!ts[i][j]&&map[i][j]=='#') { bfs(i,j); } } } cout<<ans; return 0; }
3.完全二叉樹的權值
給定一棵包含 N 個節點的完全二叉樹,樹上每個節點都有一個權值,按從上到下、從左到右的順序依次是 A1,A2,⋅⋅⋅AN,如下圖所示:
現在小明要把相同深度的節點的權值加在一起,他想知道哪個深度的節點權值之和最大?
如果有多個深度的權值和同為最大,請你輸出其中最小的深度。
注:根的深度是 1。
輸入格式
第一行包含一個整數 N。
第二行包含 N 個整數 A1,A2,⋅⋅⋅AN。
輸出格式
輸出一個整數代表答案。
資料範圍
1≤N≤105,
−105≤Ai≤105
輸入樣例:
7
1 6 5 4 3 2 1
輸出樣例:
2
解題思路:每一層的個數都是=2n-1個,而且開頭的下標都是2的倍數
程式碼:
#include<iostream> using namespace std; const int N=100010; typedef long long ll; ll a[N]; ll maxn,sum,ans; int main() { ll i,j,n,k; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; maxn=a[1]; ans=1; k=1; for(i=2;i<=n;i=i*2) { sum=0; for(j=i;j<=i*2-1&&j<=n;j++) { sum+=a[j]; } k++; if(sum>maxn) { maxn=sum; ans=k; } } cout<<ans; return 0; }