zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    (Java实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1553 数字反转(升级版)
    8.4 确定两个日期之间的月份数或年数
    (Java实现) 洛谷 P1553 数字反转(升级版)
  • 原文地址:https://www.cnblogs.com/00isok/p/9553866.html
Copyright © 2011-2022 走看看