双端队列_01bfs _ 最短路
概述
作用:
求图中只有两种权值边的最短路
。
理解:
如果我们的图中只有两种边0或者1(1也可以是任意的正数)。
每次从队头取出元素,我们就可以使用一个双端队列来模拟dij中的优先队列:
- 如果扩展到的边权值为0,就放入队头。
- 如果扩展到的边权值为1,就放入队尾。
很显然这样是满足优先队列的。因此算法是正确的。
模板代码
写法和dij的一样
vector<pii> g[N];
int dist[N];
bool vis[N];
void bfs(int be,int en){
dist[be]=0;
deque<int> q;
q.push_back(be);
while(q.size()){
int k=q.front();
q.pop_front();
if(vis[k]) continue;
vis[k]=true;
for(pii t: g[k]){
int ver=t.first,dis=t.second;
int cnt=dist[k]+dis;
if(dist[ver]>cnt){
dist[ver]=cnt;
if(dis) q.push_back(ver);
else q.push_front(ver);
}
}
}
cout<<dist[en]<<endl;
}
应用
P4554 小明的游戏 模板题
代码:
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
// #define int long long
const int N = 510,M=N*N;
int n,m;
char g[N][N];
int dist[N][N];
bool st[N][N];
int dx[]={-1,-1,1,1},dy[]={-1,1,1,-1};//相邻点
int ix[]={-1,-1,0,0},iy[]={-1,0,0,-1};//相邻边
int bfs(){
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
//四个方向正确的方向 (\\就是\ 因为是转义字符所以写两个)
char cs[]="\\/\\/";
dist[0][0]=0;
deque<pii> q;
q.push_back({0,0});
while(q.size()){
pii k=q.front();
q.pop_front();
int x=k.first,y=k.second;
if(st[x][y]) continue;
st[x][y]=true;
for(int i=0;i<4;i++){
int a=x+dx[i],b=y+dy[i];
if(a<0 || a>n || b<0 || b>m) continue;
//更新路径,求长度
int ca=x+ix[i],cb=y+iy[i];
int d=dist[x][y]+(g[ca][cb]!=cs[i]);
if(dist[a][b]>d){
dist[a][b]=d;
if(g[ca][cb]!=cs[i]) q.push_back({a,b});
else q.push_front({a,b});
}
}
}
return dist[n][m];
}
void solve()
{
cin>>n>>m;
for(int i=0;i<n;i++) cin>>g[i];
int t=bfs();
if(t==0x3f3f3f3f) puts("NO SOLUTION");
else cout<<t<<endl;
}
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
int t = 1;
cin>>t;
while (t--)
solve();
return 0;
}