题目描述

乔在迷宫中工作。不幸的是,迷宫的一部分着火了,迷宫的主人没有制定火灾的逃跑计划。请帮助乔逃离迷宫。根据乔在迷宫中的位置以及迷宫的哪个方块着火,你必须确定火焰烧到他之前,乔是否可以离开迷宫,如果能离开他能跑多快。
乔和火每分钟移动一个方格,上、下、左、右,四个方向中的一个。火势向四个方向同时蔓延。乔可以从迷宫的任何一个边界逃离迷宫。无论是乔还是火都不会到达有墙的位置。

输入

第一行输入包含一个整数,即测试次数
每个测试用例的第一行包含两个
整数R和C,用空格分隔,1≤R,C≤1000
下面R行中,每一行都包含C个字符,以及每个字符是以下之一:
# 代表墙
. 代表空地,火和乔是可通行的
J 乔在迷宫中最初的位置,火和乔是可通行的
F 代表火
在每组测试中只有一个J

输出

对于每个测试用例,如果在火蔓延的时候烧到了乔,则乔无法逃出迷宫,输出'IMPOSSIBLE'如果乔能逃出迷宫,则输出乔最快可以在几分钟内安全逃出迷宫,每组输出占一行

样例输入

2
4 4
####
#JF#
#..#
#..#
3 3
###
#J.
#.F

样例输出

3
IMPOSSIBLE

 

思路分析:这道题的关键就在于两个东西会同时移动,无法判断,所以我们想办法让他们同时移动。

以下有两个思路:

(1):两个同时进行BFS,边走边更新,但是这时有一个问题就是 样例中的 第二个 ,J是跑不出去的,为什么?他走到哪个边都会被下一步的火给烧掉,但是如果先更新 J 的路径 ,第二个样例是过不了的。所以我们先让火走,火更新与火相邻的点,然后人在走,只需要每次都看一下前面是不是火或者是不是#(不能走)就可以了。注意一些细节:

1.火可以走人走过的,不可以走火走过的(因为这样会复杂度变高),而人不能走人走过的也不能走火走过,而且两者都不能走#。

2.火在走人走过的路径之后,这个路径可以继续被火更新。

3.当人到达任意一个边界,因为是火先走的,说明火在没有封到这个边界的情况下,人已经到达了说明可以出去。

直接代码+注释:

#include <queue>
#include <cstdio>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <stack>
#include <algorithm>
#include <vector>
#define MIN(x,y) ((x)<(y)?(x) : (y))
#define MAX(x,y) ((x)>(y)?(x) : (y))
using namespace std;
typedef long long ll;
const int mod=10007;
const int INF=0x7fffffff;
const int maxn=1e6+5;
bool vis[2][1005][1005];//0 1 代表 火的路径 和 人的路径
struct node{
    int x,y,step=0;
    bool judge;//用judge判断以下是人还是火
}F[1000],Qiao;//存取多个火的起点与一个人的起点
int m,n,cnt=0;//m是行
char str[1005][1005];
int dx[5]={0,0,1,-1};
int dy[5]={-1,1,0,0};
bool check(int x,int y)
{
    if(x<1||y<1||x>m||y>n)//判界一定要判正确
        return false;
    return true;
}
int bfs()
{
    queue<node>q;
    for(int i=1;i<=cnt;i++)
    {
        q.push(F[i]);
        vis[1][F[i].x][F[i].y]=true;
    }//让火先走 
    q.push(Qiao); vis[0][Qiao.x][Qiao.y]=true;
    while(!q.empty())
    {
        node now=q.front();q.pop();
        if(now.judge==false&&(now.x==1||now.y==1||now.x==m||now.y==n))
            return now.step+1;//人到达任意边就可以
        for(int i=0;i<4;i++)
        {
            node N;
            N.x=now.x+dx[i];N.y=now.y+dy[i];
            if(now.judge==false)
            {
                if(check(N.x,N.y)&&str[N.x][N.y]=='.'&&vis[0][N.x][N.y]==false&&vis[1][N.x][N.y]==false)
                {
                    N.judge=now.judge;
                    N.step=now.step+1;
                    q.push(N);
                    vis[0][N.x][N.y]=true;
                }
            }
            else
            {
                if(check(N.x,N.y)&&vis[1][N.x][N.y]==false&&str[N.x][N.y]=='.')
                {
                    N.judge=now.judge;//火会更新人走过的路径并且让人走过的路径也变成火
                    q.push(N);
                    vis[1][N.x][N.y]=true;
                }
            }
        }
    }
    return -1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        cnt=0;//多组输入!!!我刚开始死在这了!!
        memset(str,0,sizeof(str));
        memset(vis,false,sizeof(vis));//初始化别忘记
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++)
            scanf("%s",str[i]+1);
        for(int i=1;i<=m;i++)
            for(int k=1;k<=n;k++)
            {
                if(str[i][k]=='F')
                 {
                     F[++cnt].x=i;F[cnt].y=k;
                     F[cnt].judge=true; 
                     F[cnt].step=0;
                 }
                if(str[i][k]=='J')
                {
                    Qiao.x=i;Qiao.y=k;
                    Qiao.step=0;Qiao.judge=false;
                }
            }
        int ans=bfs();
        if(ans==-1) printf("IMPOSSIBLE\n");
        else printf("%d\n",ans);
    }
    return 0;
}

然后接下来介绍另一种思路:

我们既然可以的思路是让他同时移动,所以我们可以让一个先走记录到一个点的最短时间,判断令一个走到这的时候时间大于还是小于。比如,我们让火先走BFS,去更新他扩散到每一个点的最短时间,然后我们让这个人开始跑,这个人跑到可以跑到一个点的前提是,火扩散到这个点的时间要大于人走到这的时间,这个点就可以继续走,当到边界时还需要判定以下 时间差,如果小于 火到这的最短时间,那么就出去就好了,当然这个思路也有几个细节:

1.初始化一定不能忘。

2.注意每一次都要判断以下这个点可不可以走,即要小于火到这的最短时间。

3.判断边界。

直接代码+注释:

#include <queue>
#include <cstdio>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <stack>
#include <algorithm>
#include <vector>
#define MIN(x,y) ((x)<(y)?(x) : (y))
#define MAX(x,y) ((x)>(y)?(x) : (y))
using namespace std;
typedef long long ll;
const int mod=10007;
const int INF=0x3f3f3f3f;
const int maxn=1e6+5;
struct node{
    int x,y,step=0;
}F[1000],Qiao;
int m,n,cnt=0;//m是行
int num[1005][1005];//记录火更新扩散的时间
char str[1005][1005];//地图
int dx[5]={0,0,1,-1};
int dy[5]={-1,1,0,0};
bool vis[1005][1005];//标记人走过的路径
int bfs()
{
    queue<node>q;
    while(!q.empty()) q.pop();
    q.push(Qiao);
    vis[Qiao.x][Qiao.y]=true;//不要忘记标记
    while(!q.empty())
    {
        node pre=q.front();q.pop();
        if(pre.x==1||pre.y==1||pre.x==m||pre.y==n)//只要碰到边界就可以了。
            return pre.step+1;
        for(int i=0;i<4;i++)
        {
            int mx=pre.x+dx[i],my=pre.y+dy[i];
            if(vis[mx][my]==false&&mx>=1&&mx<=m&&my>=1&&my<=n&&pre.step+1<num[mx][my]&&str[mx][my]!='#')//判断人可不可以走
            {
                node now;
                now.x=mx;now.y=my;now.step=pre.step+1;
                q.push(now);
                vis[mx][my]=true;
            }
        }
    }
    return -1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        cnt=0;
        scanf("%d%d",&m,&n);
        memset(num,INF,sizeof(num));
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=m;i++)
            scanf("%s",str[i]+1);
        for(int i=1;i<=m;i++)
            for(int k=1;k<=n;k++)
            {
                if(str[i][k]=='F')
                {
                    F[++cnt].x=i;
                    F[cnt].y=k;
                    F[cnt].step=0;
                }
                if(str[i][k]=='J')
                {
                    Qiao.x=i;Qiao.y=k;
                    Qiao.step=0;
                }
            }
        queue<node>q;
        while(!q.empty()) q.pop();
        for(int i=1;i<=cnt;i++)
        {
            q.push(F[i]);
            num[F[i].x][F[i].y]=F[i].step;
        }
        while(!q.empty())//首先让火更新时间
        {
            node pre=q.front();q.pop();
            for(int i=0;i<4;i++)
            {
                int mx=pre.x+dx[i],my=pre.y+dy[i];
                if(mx>=1&&my>=1&&mx<=m&&my<=n&&str[mx][my]!='#'&&num[mx][my]==INF)//判断这里能不能更新
                {
                    node now;
                    now.x=mx;now.y=my;now.step=pre.step+1;
                    num[mx][my]=now.step;
                    q.push(now);
                }
            }
        }
        /*for(int i=1;i<=m;i++)
        {
            for(int k=1;k<=n;k++)
                printf("%d ",num[i][k]);
            printf("\n");
        }*/
        int ans=bfs();
        if(ans==-1) printf("IMPOSSIBLE\n");
        else printf("%d\n",ans);
    }
    return 0;
}

还有很多细节问题,具体问题可以评论交流,祝大家一次AC;