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

  • 相关阅读:
    DS博客作业04--图
    DS博客作业03--树
    DS博客作业02--栈和队列
    DS01-线性表
    c博客06-结构体&文件
    C博客作业05--指针
    123
    面向对象设计大作业
    购物车
    有理数类的设计
  • 原文地址:https://www.cnblogs.com/00isok/p/9553866.html
Copyright © 2011-2022 走看看