【0-1BFS】双端队列BFS
【0-1BFS】双端队列BFS
适用范围
可转换为边权值的最短路问题
边权值为可能有,也可能没有
eg 在走迷宫问题中,你可以花 1 个金币走 5 步,也可以不花金币走 1 步
思路
把没有权值的边扩展到的点放到队首,有权值的边扩展到的点放到队尾
->可保证像普通 BFS 一样整个队列队首到队尾权值单调不下降
题目积累
Takahashi the Wall Breaker
https://atcoder.jp/contests/abc400/tasks/abc400_d
注意这题题目表述比较抽象:要看英文
翻译:可以只破坏一面墙,也可以连续破坏两面墙->但是花费相同 都是1
有一点点最短路思想
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=1010;
int h,w;
string s[N];
int a,b,c,d;
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
bool st[N][N];
int dis[N][N];
bool is_in(int x,int y){
return x>=0 && x<h && y>=0 && y<w;
}
void solve(){
cin>>h>>w;
for(int i=0;i<h;i++){
cin>>s[i];
}
cin>>a>>b>>c>>d;
a--;b--;c--;d--;
deque<PII> q;
memset(dis,-1,sizeof dis);
dis[a][b]=0;
q.push_front({a,b});
while(q.size()){
PII t=q.front();
q.pop_front();
int px=t.first,py=t.second;
if(st[px][py]) continue;
st[px][py]=1;
//1:踢1格/2:踢2格->都是同一种花费:1
for(int i=0;i<4;i++){
for(int j=1;j<=2;j++){
int nx=px+dx[i]*j,ny=py+dy[i]*j;
if(!is_in(nx,ny)) continue;
int v=1;
if(j==1 && s[nx][ny]=='.') v=0;//不需要踢+不需要花费
if(dis[nx][ny]==-1 || dis[nx][ny]>dis[px][py]+v){
dis[nx][ny]=dis[px][py]+v;
//【01BFS特点】如果有花费:队尾/无花费:队头
if(v){
q.push_back({nx,ny});
}
else{
q.push_front({nx,ny});
}
}
}
}
}
cout<<dis[c][d];
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T=1;
//cin>>T;
while(T--) solve();
return 0;
}
Chamber of Secrets
https://codeforces.com/problemset/problem/173/B
思路
一个方向射出不需要花费(0),而往四个方向射出需要花费(1)
注意遇到#只改方向 移动在取头来操作
const int N=1010;
const int INF=0x3f3f3f3f;
int n,m;
string s[N];
int dis[N][N][5];//再开一维记录方向
/*
遇到#:是只往一个方向(不花费)还是往4个方向(花费)
要从(0,0)到(n-1,m-1)
*/
struct node{
int x,y,dir;
};
deque<node> q;
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
/*注意开头和结尾都只能右出!!!dir==0*/
/*【注意起点和终点】
"蛇怪位于网格右下方单元格的右侧,并向左看(在左下方单元格的方向)"->要从蛇怪开始往前找!
人是在0 0点往右看->终点为**反方向**
*/
void solve(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>s[i];
}
//初始化成INF:找最短路
memset(dis,0x3f,sizeof dis);
deque<node> q;
q.push_front({n-1,m-1,2});
dis[n-1][m-1][2]=0;
while(q.size()){
node t=q.front();
q.pop_front();
int x=t.x,y=t.y,dir=t.dir;
int d=dis[x][y][dir];
//cout<<x<<" "<<y<<" "<<dir<<endl;
//能花费的条件:遇到#->只改方向->还用x y
if(s[x][y]=='#'){
for(int i=0;i<4;i++){
if(i!=dir){
if(d+1<dis[x][y][i]){
q.push_back({x,y,i});
dis[x][y][i]=d+1;
}
}
}
}
//往一个方向走
int nx=x+dx[dir],ny=y+dy[dir];
//cout<<nx<<" "<<ny<<endl;
if(nx<0 || nx>=n || ny<0 || ny>=m) continue;//注意使用了continue就要后放:先改的方向
//不花费:放前面
if(d<dis[nx][ny][dir]){
q.push_front({nx,ny,dir});
dis[nx][ny][dir]=d;
}
}
if(dis[0][0][2]==INF){
cout<<"-1"<<endl;
}
else cout<<dis[0][0][2]<<endl;
return;
}