推箱子第二部分(两次bfs)

174. 推箱子 - AcWing题库

一直改bug到现在,幸亏最终还是过了,不然真就通宵了,,,,室友都睡了,抓紧时间发文章,坚持一天一篇。虽然今天已经过了12点,但是没睡觉还是算今天的。哈哈哈哈,不知道自己在说什么。

两次bfs,不能把人和箱子写在一个结构体看成一个整体状态,因为要保证箱子移动最少的前提下,人移动最少。因此先对箱子做bfs,相应的对人做bfs,确实有点难,一整个看不懂可以一部分一部分的看,首先把问题拆解,第一个问题是一个人从start位置开始走到end位置,这是一个经典又简单的bfs,然后写箱子从start到end,但要通过人的bfs判断是否箱子可以有这样的走法。然后反向求出路径,再将路径倒序输出就是结果了。

#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=25,M=25;
int dx[4]= {-1,1,0,0},dy[4]= {0,0,-1,1};
int n,m;
char g[N][M];
char ops[5]="nswe";
int pre_man[N][M];
PII dist[N][M][4];
vector<int> path[N][M][4];
bool st[N][M][4],vis[N][M];
struct Point {
    int x,y,dir;
} pre[N][M][4];
bool check(int a,int b) {
    return a>=1&&a<=n&&b>=1&&b<=m&&g[a][b]!='#';
}
int bfs_man(PII start,PII end,PII box,vector<int>&seq) {
    memset(vis,0,sizeof(vis));
    memset(pre_man,-1,sizeof(pre_man));
    queue<PII>q;
    q.push(start);
    vis[start.first][start.second]=true;
    vis[box.first][box.second]=true;
    while(q.size()) {
        PII t=q.front();
        q.pop();
        int x=t.first,y=t.second;
        if(t==end) {
            seq.clear();
            while(~pre_man[x][y]) {
                int d=pre_man[x][y];
                seq.push_back(d);
                x-=dx[d],y-=dy[d];
            }
            return seq.size();
        }
        for(int i=0; i<4; i++) {
            int cx=x+dx[i],cy=y+dy[i];
            if(!check(cx,cy))continue;
            if(vis[cx][cy])continue;
            vis[cx][cy]=true;
            pre_man[cx][cy]=i;
            q.push({cx,cy});
        }
    }
    return -1;
}
bool bfs_box(PII start,PII box,Point &end) {
    memset(st,0,sizeof(st));
    queue<Point>q;
//因为从Point这个结构体中可以推断出此时箱子和人的距离
//但是第一次的人的位置根据结构体无法推出,所以把第一次箱子移动后的状态都放入队列中
    for(int i=0; i<4; i++) {
        int a=box.first+dx[i],b=box.second+dy[i];//箱子
        int c=box.first-dx[i],d=box.second-dy[i];//人
        if(!check(a,b)||!check(c,d))continue;
        vector<int>seq;
        int len=bfs_man(start, {c,d},box,seq);
        if(len==-1)continue;
        path[a][b][i]=seq;
        st[a][b][i]=true;
        dist[a][b][i]= {1,len};
        pre[a][b][i]= {box.first,box.second,-1}; //设为-1是为了之后遍历时dir为-1代表箱子移动第一步
        q.push({a,b,i});
    }
    PII mind= {1e9,1e9};
    while(q.size()) {
        Point t=q.front();
        q.pop();
        if(g[t.x][t.y]=='T'&&mind>dist[t.x][t.y][t.dir]) {
            end=t;
            mind=dist[t.x][t.y][t.dir];
        }
        for(int i=0; i<4; i++) {
            int a=t.x+dx[i],b=t.y+dy[i];//箱子
            int c=t.x-dx[i],d=t.y-dy[i];//人
            if(!check(a,b)||!check(c,d))continue; 
            vector<int>seq;
            int len=bfs_man({t.x-dx[t.dir],t.y-dy[t.dir]}, {c,d}, {t.x,t.y},seq);
            if(len==-1)continue;
            PII curd= {dist[t.x][t.y][t.dir].first+1,dist[t.x][t.y][t.dir].second+len};
            if(!st[a][b][i]) {
                path[a][b][i]=seq;
                st[a][b][i]=true;
                dist[a][b][i]=curd;
                pre[a][b][i]=t;
                q.push({a,b,i});
            } else if(dist[a][b][i]>curd) {
                path[a][b][i]=seq;
                dist[a][b][i]=curd;
                pre[a][b][i]=t;
            }
        }
    }
    return mind.first!=1e9;
}
int main(void) {
    int T=1;
    while(T) {
        scanf("%d%d",&n,&m);
        if(!n&&!m) {
            break;
        }
        for(int i=1; i<=n; i++) {
            cin>>g[i]+1;
        }
        PII start,box;
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=m; j++) {
                if(g[i][j]=='S')start= {i,j};
                else if(g[i][j]=='B')box= {i,j};
            }
        }
        Point end;
        string res;
        if(!bfs_box(start,box,end))res="Impossible.";
        else {
            while(~end.dir) {
                res+=ops[end.dir]-32;
                for(int i=0; i<path[end.x][end.y][end.dir].size(); i++) {
                    res+=ops[path[end.x][end.y][end.dir][i]];
                }
                end=pre[end.x][end.y][end.dir];
            }
            reverse(res.begin(),res.end());
        }
        printf("Maze #%d\n",T);
        cout<<res<<endl<<endl;
        T++;
    }
    return 0;
}
  1. 代码包含了一些标准的C++库,如<iostream><queue><cstring><vector><algorithm>

  2. 定义了常量NM,它们的值为25。这些常量用于指定迷宫的最大尺寸。

  3. 定义了数组dxdy,表示四个可能的移动方向(北、南、西、东)。

  4. 声明了变量nm,用于存储迷宫的尺寸。

  5. 定义了字符数组ops,其中包含字符'nswe',用于表示四个可能的移动方向。

  6. 声明了一个二维字符数组g[N][M],用于表示迷宫的网格。

  7. 声明了用于存储关于距离、路径和迷宫状态信息的数组和变量。

  8. 定义了一个Point结构,用于表示人当前的位置和他们面朝的方向。

  9. 定义了一个check函数,用于检查给定的位置是否有效(在界限内且不是障碍物)。

  10. 定义了一个bfs_man函数,执行广度优先搜索,以找到从人当前位置到目标位置的最短路径,同时避免箱子的位置。

  11. 定义了一个bfs_box函数,执行广度优先搜索,以找到箱子到达目标位置的最短路径。它还计算了箱子和人在每一步的距离和路径。

  12. main函数读取输入,并且主要的迷宫求解逻辑位于其中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值