UVA 11624-Fire!【双BFS】

<题目链接>

题目大意:

你的任务是帮助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
");
        else 
            printf("%d
", 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
",ans+1);
        }
        else printf("IMPOSSIBLE
");
    }
    return 0;
}
View Code

2018-08-29

原文地址:https://www.cnblogs.com/00isok/p/9553866.html