题解 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; }
点个赞再走吧!
不要妄图追上西坠的太阳,而是要在黎明前就等着它!