淺談雙端隊列廣搜
阿新 • • 發佈:2018-09-09
添加 擴展 [] amp 多少 space [1] 彈出 c-c
什麽是雙端隊列BFS?
如果你不了解雙端隊列 deque
的話,請先去學習。
雙端隊列 BFS
又稱 0-1 BFS
適用範圍
邊權值為可能有,也可能沒有(由於 BFS
適用於權值為 \(1\) 的圖,所以一般是 \(0\) \(or\) \(1\)),或者能夠轉化為這種邊權值的最短路問題。
例如在走迷宮問題中,你可以花 \(1\) 個金幣走 \(5\) 步,也可以不花金幣走 \(1\) 步,這就可以用 0-1 BFS
解決。
實現
把沒有權值的邊擴展到的點放到隊首,有權值的邊擴展到的點放到隊尾。
下面是偽代碼:
while(隊列不為空) { int u = 隊首; 彈出隊首; for(枚舉 u 的鄰居) { 更新數據 if(...) 添加到隊首; else 添加到隊尾; } }
Croc Champ 2012 - Round 1 B Chamber of Secrets
翻譯
一個 \(n \times m\) 的圖,現在有一束激光從左上角往右邊射出,每遇到 ‘#‘,你可以選擇光線往四個方向射出,或者什麽都不做,問最少需要多少個 ‘#‘ 往四個方向射出才能使光線在第 \(n\) 行往右邊射出。
思路
此題目正解不是 0-1 BFS
但是適用 0-1 BFS
可以不需要思考過程,賽時許多大佬都是這麽做的。
做法很簡單,一個方向射出不需要花費(\(0\)),而往四個方向射出需要花費(\(1\)),然後直接來就可以了。
Code
#include<bits/stdc++.h> using namespace std; #define INF (1<<29) int n,m; char grid[1001][1001]; int dist[1001][1001][4]; int vis[1001][1001][4]; int fx[]={1,-1,0,0}; int fy[]={0,0,1,-1}; deque <int> q; void add_front(int x,int y,int dir,int d) { if(d<dist[x][y][dir]) { dist[x][y][dir]=d; q.push_front(dir); q.push_front(y); q.push_front(x); } } void add_back(int x,int y,int dir,int d) { if(d<dist[x][y][dir]) { dist[x][y][dir]=d; q.push_back(x); q.push_back(y); q.push_back(dir); } } int main() { cin>>n>>m; for(int i=0;i<n;i++) cin>>grid[i]; for(int i=0;i<n;i++) for(int j=0;j<m;j++) for(int k=0;k<4;k++) dist[i][j][k]=INF; add_front(n-1,m-1,3,0); while(!q.empty()) { int x=q[0],y=q[1],dir=q[2]; q.pop_front(); q.pop_front(); q.pop_front(); if(vis[x][y][dir]) continue; vis[x][y][dir]=true; int d=dist[x][y][dir]; int nx=x+fx[dir],ny=y+fy[dir]; if(nx>=0&&nx<n&&ny>=0&&ny<m) add_front(nx,ny,dir,d); if(grid[x][y]=='#') for(int i=0;i<4;i++) if(i!=dir) add_back(x,y,i,d+1); } if(dist[0][0][3]==INF) cout<<-1<<endl; else cout<<dist[0][0][3]<<endl; return 0; }
淺談雙端隊列廣搜