双向BFS

作用

双向BFS本质还是bfs,不过是在已知起点和终点的状况下从起点和终点两头开始bfs,这样可以节省时间避免单bfs时所带来的组合爆炸,而且在解决起点和终点均可移动且移动速度不同等鬼畜问题时有奇效

模板
#include<bits/stdc++.h>
#define ll long long
queue<类型> qu[2];
bool bfs(int id){
    int s = qu[id].size();//每次只扩展当前这一层
    while(s--){
        类型 str = qu[id].front(),nex;
        qu[id].pop();
    for(){
    	//进行扩展和判断
    	if(vis[1 - id][x])return true;//如果这个点对方访问过就返回true
    }
    return false;
}
int solve(){
	//一系列预处理操作
    memset(vis,0,sizeof vis);
    while(!qu[0].empty())qu[0].pop();
    while(!qu[1].empty())qu[1].pop();
    vis[0][getInt(st)] = true;
    vis[1][getInt(ed)] = true;
    qu[0].push(st);
    qu[1].push(ed);
    int tim = 0;//计时器用于判断走了多少步
    if()return tim;//如果开始就等于最终就直接返回
    while(!qu[0].empty() || !qu[1].empty()){
        tim++;//时间加1跑第一个bfs
        if(bfs(0))return tim;
        tim++;//时间加1跑第二个bfs
        if(bfs(1))return tim;
    }
}
例题:

Open the lock

#include<bits/stdc++.h>
#define ll long long
#define eps 1e-8
#define INF 0x3f3f3f3f
#define pb push_back
#define endl '\n'
#define IO ios::sync_with_stdio(false)
#pragma
using namespace std;
const ll maxn = 25;
const ll mod = 1e8 + 7;
int t,f[4] = {1,10,100,1000};
string st,ed;
bool vis[2][10000];
queue<string> qu[2];

int getInt(string str){ //返回整数
    int ans = 0;
    for(int i = 0; i <= 4; i++)
        ans += (f[3 - i] * (str[i] - '0'));
    return ans;
}
bool bfs(int id){
    int s = qu[id].size();
    while(s--){
        string str = qu[id].front(),nex;
        qu[id].pop();
        for(int i = 0; i < 3; i++){//交换左右操作
            nex = str;
            swap(nex[i],nex[i + 1]);
            int g = getInt(nex);
            if(!vis[id][g]){
                vis[id][g] = true;
                qu[id].push(nex);
            }
            if(vis[1 - id][g])return true;
        }
        for(int i = 0; i < 4; i++){//加减操作
            nex = str;
            nex[i] = (char)((nex[i] - '1' + 1) % 9 + '1');
            int g = getInt(nex);
            if(!vis[id][g]){
                vis[id][g] = true;
                qu[id].push(nex);
            }
            if(vis[1 - id][g])return true;
            nex = str;
            nex[i] = (char)((nex[i] - '1' - 1 + 9) % 9 + '1');
            g = getInt(nex);
            if(!vis[id][g]){
                vis[id][g] = true;
                qu[id].push(nex);
            }
            if(vis[1 - id][g])return true;
        }
    }
    return false;
}
int solve(){
    memset(vis,0,sizeof vis);
    while(!qu[0].empty())qu[0].pop();
    while(!qu[1].empty())qu[1].pop();
    vis[0][getInt(st)] = true;
    vis[1][getInt(ed)] = true;
    qu[0].push(st);
    qu[1].push(ed);
    int tim = 0;
    if(ed == st)return tim;
    while(!qu[0].empty() || !qu[1].empty()){
        tim++;
        if(bfs(0))return tim;
        tim++;
        if(bfs(1))return tim;
    }
}
int main(){
    IO;
    cin >> t;
    while(t--){
        cin >> st >> ed;
        cout << solve() << endl;
    }
    return 0;
}

Nightmare
上一题直接套了模板,这一题则是需要变换一下了,首先题目中鬼是可以扩张的,而且无视了墙,所以不需要把鬼放到队列里bfs,只需要用曼哈顿距离判断是否与人重合即可

#include<bits/stdc++.h>
#define ll long long
#define eps 1e-8
#define INF 0x3f3f3f3f
#define pb push_back
#define endl '\n'
#define IO ios::sync_with_stdio(false)
#pragma
using namespace std;
const ll maxn = 25;
const ll mod = 1e8 + 7;
int t,n,m,x[4] = {0,0,-1,1},y[4] = {1,-1,0,0};
bool vis[2][805][805];
char mp[805][805];
struct node{
    int x,y;
}M,G,z[2];
queue<node> qu[2];

bool judje(int tim, node x){//判断人时都会被鬼抓住
    int f = INF;
    for(int i = 0; i < 2; i++)
        f = min(f,abs(x.x - z[i].x) + abs(x.y - z[i].y));
    if(tim * 2 >= f)return true;
    else return false;
}
bool bfs(int id,int tim){
    int s = qu[id].size();
    while(s--){
        node t = qu[id].front();
        qu[id].pop();
        if(judje(tim,t))continue;//鬼先扩展
        for(int i = 0; i < 4; i++){
            int xx = x[i] + t.x,yy = y[i] + t.y;
            if(xx >= 1 && yy >= 1 && xx <= n && yy <= m){
                if(!vis[id][xx][yy] && !judje(tim,{xx,yy}) && (mp[xx][yy] == '.' || mp[xx][yy] == 'M' || mp[xx][yy] == 'G')){
                    vis[id][xx][yy] = true;
                    if(vis[1 - id][xx][yy])return true;
                    qu[id].push({xx,yy});
                }
            }
        }
    }
    return false;
}
int solve(){
    memset(vis,0,sizeof vis);
    int tim = 0;
    while(!qu[0].empty())qu[0].pop();
    while(!qu[1].empty())qu[1].pop();
    vis[0][G.x][G.y] = true;
    vis[1][M.x][M.y] = true;
    qu[0].push(G);
    qu[1].push(M);
    while(!qu[0].empty() || !qu[1].empty()){
        tim++;
        for(int i = 1; i <= 3; i++)//男的可以走三次,所以直接bfs3次这里就能体现出来双向的优势了
            if(bfs(1,tim))return tim;
        if(bfs(0,tim))return tim;
    }
    return -1;
}
int main(){
    int g = 0;
    scanf("%d",&t);
    while(t--){
        g = 0;
        scanf("%d %d",&n,&m);
        for(int i = 1; i <= n; i++){
            scanf("%s",mp[i] + 1);
            for(int j = 1; j <= m; j++){
                if(mp[i][j] == 'M')M.x = i,M.y = j;
                else if(mp[i][j] == 'G')G.x = i,G.y = j;
                else if(mp[i][j] == 'Z')z[g].x = i,z[g++].y = j;
            }
        }
        printf("%d\n",solve());
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值