题解 UVA11624 【Fire!】

为了本题解,蒟蒻特地将代码风格改了改,让大家看得清晰一些。

看了一下其他的题解,说明都不是很详细,于是我来为萌新们说明一下。

我先讲讲我的思路:这道题我们需要用两个 BFS,我们分别来看一看。

第一个 BFS

第一个 BFS作为初始化,作用是将火焰蔓延到某个点(i,j)所需要的时间统计出来,记作 Fire[i][j] 。

初始时每个格子只需要设成N*M即可,因为最大也不过如此。

如果某个点(i,j)是墙,或是火源,那么 ]Fire[i][j] =0;

然后利用队列,初始时先将所有火源压进队列。然后进行 BFS,不难推出每个点在第几分钟会着火。

第二个 BFS

第二个 BFS更重要,作用是计算人是否可以逃出迷宫。当然,也需要用到队列。

对于每个点,我们需要记录跑到该点用了多少时间。如果火焰比人到得更早,或是同时到达,那么就不能往这里走。

而且,我们要避免重复走到某个点。那么不妨开一个Vis数组,用来记录有没有来过这个点。

然后每次取出队首,向四周扩散,必须判断一下能否在火焰之前到达。

如果跑到了边界,那么就输出当前的时间再加1。之所以加1,是因为根据题意,你必须在花一分钟从边界跑出迷宫。然后就不用继续做了,跳出循环。在跳出循环之前,先记录下已经找到了解。

如果搜完了却没找到解,那么输出 IMPOSSIBLE 。

希望我讲的够详细,没听懂的可以继续往下看,代码中有注释,也可以私信我哦

#include<bits/stdc++.h>
using namespace std;
struct Struct
{
    int X;//当前横坐标
    int Y;//当前纵坐标
    int Step;//当前时间
};
Struct Xy[1000001];
int Case;//数据组数
int N,M;//行、列
int Dx,Dy;//下一个坐标
bool GetAns;//记录是否找到解
bool Vis[1001][1001];//记录是否来过某点
char Map[1001][1001];//输入进来的地图
int Fire[1001][1001];//记录火焰到某个点所需要的时间
int Dir[4][2]={1,0,0,1,-1,0,0,-1};//每次往相邻的四个方向拓展
Struct First;//人物初始位置
Struct Nxt;//下一个点
Struct Now;//当前点
int main()
{
    int i,j;
    cin>>Case;
    while(Case--)//多组数据
    {
        queue<Struct>Q;//这就是队列
        cin>>N>>M;
        for(i=1;i<=N;i++)
        {
            for(j=1;j<=M;j++)
            {
                Fire[i][j]=N*M;//刚开始现将时间设的够大,方便取最小值
                Vis[i][j]=false;//刚开始每个点都没去过
            }
        }
        for(i=1;i<=N;i++)
        {
            for(j=1;j<=M;j++)
            {
                cin>>Map[i][j];//输入当前点的地形
                if(Map[i][j]=='F')
                {
                    Nxt.X=i;
                    Nxt.Y=j;
                    Nxt.Step=0;
                    Fire[i][j]=0;
                    Q.push(Nxt);//如果是火源,加入队列
                }
                if(Map[i][j]=='#')
                {
                    Fire[i][j]=0;//如果是墙壁,那么无法到达
                }
                if(Map[i][j]=='J')
                {
                    First.X=i;//记录人物初始横坐标
                    First.Y=j;//记录人物初始纵坐标
                    First.Step=0;
                }
            }
        }
        while(!Q.empty())//第一个BFS
        {
            Now=Q.front();//取出队首
            Q.pop();//弹掉队首
            for(i=0;i<4;i++)//向四周扩散
            {
                Dx=Now.X+Dir[i][0];//下一个点的横坐标
                Dy=Now.Y+Dir[i][1];//下一个点的纵坐标
                if(Now.Step+1<Fire[Dx][Dy])//取较小值
                {
                    Fire[Dx][Dy]=Now.Step+1;//更新较小值
                    Nxt.X=Dx;
                    Nxt.Y=Dy;
                    Nxt.Step=Now.Step+1;
                    Q.push(Nxt);//继续BFS
                }
            }
        }
        Q.push(First);//将人物初始位置加入队列
        GetAns=false;//先设为未找到答案
        while(!Q.empty())//第二个BFS
        {
            Now=Q.front();//取出队首
            Q.pop();//弹掉队首
            if(Now.X==1||Now.Y==1||Now.X==N||Now.Y==M)//如果到了边界,结束搜索
            {
                GetAns=true;//设为已经找到了解
                cout<<Now.Step+1<<endl;//输出步数,记得+1
                break;//结束搜索
            }
            for(i=0;i<4;i++)//向四周扩散
            {
                Dx=Now.X+Dir[i][0];//下一个点的横坐标
                Dy=Now.Y+Dir[i][1];//下一个点的纵坐标
                if(Now.Step+1<Fire[Dx][Dy]&&!Vis[Dx][Dy])//是否去过,并判断能否进去
                {
                    Vis[Dx][Dy]=true;//标记为去过了
                    Nxt.X=Dx;
                    Nxt.Y=Dy;
                    Nxt.Step=Now.Step+1;
                    Q.push(Nxt);//加入队列
                }
            }
        }
        if(!GetAns)//如果没有找到答案,输出IMOPOSSIBLE
        {
            cout<<"IMPOSSIBLE"<<endl;
        }
    }
    return 0;
}
View Code

点个赞再走吧!

posted @ 2020-08-08 15:08  Bushuai_Tang  阅读(177)  评论(0编辑  收藏  举报