【0-1BFS】双端队列BFS

【0-1BFS】双端队列BFS

适用范围

可转换为边权值的最短路问题
边权值为可能有,也可能没有
eg 在走迷宫问题中,你可以花 1 个金币走 5 步,也可以不花金币走 1 步

思路

把没有权值的边扩展到的点放到队首,有权值的边扩展到的点放到队尾
->可保证像普通 BFS 一样整个队列队首到队尾权值单调不下降
image

题目积累

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;
}
posted @ 2025-04-11 13:38  White_ink  阅读(3)  评论(0)    收藏  举报
  1. 1 Particles Tony Anderson
Particles - Tony Anderson
00:00 / 00:00
An audio error has occurred.

Not available