<题目链接>
题目大意:
你的任务是帮助J走出一个大火蔓延的迷宫。J每分钟可以超上下左右四个方向移动,而所有着火的格子每一分钟都会往四个方向蔓延一格。迷宫中有一些障碍,J和火都无法进入。当J走出迷宫的边界时,逃离成功。
解题分析:
注意,刚开始不一定只有一个格子着火。看到这道题,很容易想到要用BFS,火的蔓延和人的行走都用BFS模拟一下。先将火蔓延到所有点的最短时间通过bfs模拟求出来,然后再对人行走的过程用bfs进行模拟,从而计算出该人到达每个点的最短时间,如果人到达该点的最短时间晚于火到达的最短时间,那么该点不能走。根据这些,判断该人是否能够达到边界,即逃出迷宫。
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; const int MAXN = 1005; const int INF = 0x3f3f3f3f; const int dx[] = {-1, 0, 0, 1}; const int dy[] = {0, -1, 1, 0}; struct node{ node (int xx, int yy, int dd) { x = xx; y = yy; d = dd; } int x, y, d; }; char g[MAXN][MAXN]; int vf[MAXN][MAXN], vm[MAXN][MAXN]; int T[MAXN][MAXN]; //记录火蔓延到的点的最短时间,以便判断人是否是在火烧到该点之前就能够到达该点 int r, c, sx, sy; queue<node> qf; void bfs_fire() { //bfs模拟火的蔓延过程 while (!qf.empty()) { node u = qf.front(); qf.pop(); node v = u; for (int i = 0; i < 4; i++){ int tx = u.x + dx[i]; int ty = u.y + dy[i]; if (tx < 0 || tx >= r || ty < 0 || ty >= c || g[tx][ty] == '#') continue; if (!vf[tx][ty]) { vf[tx][ty] = 1; T[tx][ty] = u.d+1; //记录火蔓延到的点的最短时间,以便判断人是否是在火烧到该点之前就能够到达该点 qf.push(node(tx,ty,u.d+1)); } } } } int bfs_man() { queue<node> qm; while (!qm.empty()) qm.pop(); node s(sx, sy, 0); qm.push(s); memset(vm, 0, sizeof(vm)); vm[sx][sy] = 1; while (!qm.empty()) { node u = qm.front(); qm.pop(); if (u.x == 0 || u.x == r - 1 || u.y == 0 || u.y == c - 1) //达到边界 return u.d + 1; node v = u; for (int i = 0; i < 4; i++) { int tx = u.x + dx[i]; int ty = u.y + dy[i]; if (tx < 0 || tx >= r || ty < 0 || ty >= c || g[tx][ty] == '#') continue; if (u.d + 1 >= T[tx][ty]) continue; //如果在这个人来之前,火就烧到了这里,则这个点不能走,这里很关键 if (!vm[tx][ty]){ //如果这个点能走且该人之前没走过 vm[tx][ty] = 1; qm.push(node(tx,ty,u.d+1)); } } } return -1; } int main() { int cas; scanf("%d", &cas); while (cas--) { scanf("%d%d", &r, &c); for (int i = 0; i < r; i++) scanf("%s", g[i]); while(!qf.empty()) qf.pop(); memset(vf, 0, sizeof(vf)); memset(T, INF, sizeof(T)); for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { if (g[i][j] == 'J') { sx = i; sy = j; } if (g[i][j] == 'F') { //注意,着火点不止有一个 node tmp(i, j, 0); vf[i][j] = 1; T[i][j] = 0; qf.push(tmp); } } } bfs_fire(); //先提前计算,火能够蔓延到的点的最短时间 int ans = bfs_man(); if (ans == -1) printf("IMPOSSIBLE\n"); else printf("%d\n", ans); } return 0; }
下面还有一种想法:
通过模拟,火先蔓延一步,人再走一步,因为只有最新蔓延的火,才会对下一步的蔓延到其他点做出贡献,所以,每次只记录新蔓延出来的点,如下面的que[]数组模拟队列,用head和topend来控制只对新蔓延的点进行烧火模拟,达到“火蔓延一步”,“人走一步”的效果。但是下面的这段代WA了,不知道是不是我的思路有问题,先记录着。
#include <cstdio> #include <cstring> #include <queue> #include <iostream> #include <algorithm> using namespace std; const int maxn=1000+10; int n,m,flag,ans; char mpa[maxn][maxn]; int vis[maxn][maxn]; int head,topend; int dir[][2]={1,0,0,1,-1,0,0,-1}; struct node{ int x,y; int step; node(int a=0,int b=0,int c=0){ x=a,y=b,step=c; } }st,et,que[maxn*maxn]; void fire(){ //每次只更新一步 int top=topend; while(head<top){ //上一次刚蔓延到的所有点,用小于是因为每次蔓延到新的点都是topend++ node now=que[head]; head++; for(int k=0;k<4;k++){ int xx=now.x+dir[k][0]; int yy=now.y+dir[k][1]; //超界或者已经蔓延过的点,就不用再蔓延了 if(xx<1||xx>n||yy<1||yy>m||mpa[xx][yy]!='.')continue; mpa[xx][yy]='*'; que[topend++]=node(xx,yy,now.step); //火烧的过程不算在走的步数中 } } } void bfs(){ memset(vis,0,sizeof(vis)); queue<node>q; vis[st.x][st.y]=1; q.push(st); while(!q.empty()){ node now=q.front(); q.pop(); if(now.x==1||now.x==n||now.y==1||now.y==m){ flag=1; ans=now.step; return; } fire(); //模拟烧火过程 for(int k=0;k<4;k++){ int xx=now.x+dir[k][0]; int yy=now.y+dir[k][1]; if(xx<1||xx>n||yy<1||yy>m||vis[xx][yy]||mpa[xx][yy]!='.')continue; vis[xx][yy]=1; q.push(node(xx,yy,now.step+1)); } } } int main(){ int t;scanf("%d",&t); while(t--){ scanf("%d %d",&n,&m); memset(mpa,0,sizeof(mpa)); memset(que,0,sizeof(que)); head=topend=0; for(int i=1;i<=n;i++){ scanf("%s",mpa[i]+1); for(int j=1;j<=m;j++){ if(mpa[i][j]=='J'){ st=node(i,j,0); mpa[i][j]='.'; } if(mpa[i][j]=='F'){ et=node(i,j,0); que[topend++]=et; //着火点不止有一个 mpa[i][j]='*'; } } } flag=0; bfs(); if(flag){ printf("%d\n",ans+1); } else printf("IMPOSSIBLE\n"); } return 0; }
2018-08-29