Escape BNUOJ 49278

Name: Escape
P_ID: BNUOJ 49278
题目描述:小明进入地下迷宫寻找宝藏,找到宝藏后却发生地震,迷宫各处产生岩浆,小明急忙向出口处逃跑。如果丢下宝藏,小明就能迅速离开迷宫,但小明并不想轻易放弃自己的辛苦所得。所以他急忙联系当程序员的朋友你(当然是用手机联系),并告诉你他所面临的情况,希望你能告诉他是否能成功带着宝藏逃脱。
Input:
有多组测试数据。

每组测试数据第一行是一个整数T,代表接下去的例子数。(0<=T<=10)

接下来是T组例子。

每组例子第一行是两个整数N和M。代表迷宫的大小有N行M列(0<=N,M<=1000)。

接下来是一个N*M的迷宫描述。

S代表小明的所在地。

E代表出口,出口只有一个。

‘.’代表可以行走的地方。

‘!’代表岩浆的产生地。(这样的地方会有多个,其个数小于等于10000)

‘#’代表迷宫中的墙,其不仅能阻挡小明前进也能阻挡岩浆的蔓延。

小明携带者宝藏每秒只能向周围移动一格,小明不能碰触到岩浆(小明不能和岩浆处在同一格)。

岩浆每秒会向四周不是墙的地方蔓延一格。

小明先移动完成后,岩浆才会蔓延到对应的格子里。

小明能移动到出口,则小明顺利逃脱。

Output:
每组测试数据输出只有一行“Yes”或者“No”。 “Yes”代表小明可以成功逃脱。否则输出“No”。

Sample Input

3
5 5
....!
S....
#....
!#...
#E...
2 2
S.
!E
2 2
SE
!.

Sample Output

Yes
No
Yes

这个题一开始没有自己写出来,看了题解才知道,不过作为第一次遇到双重bfs问题,还是很受用的。

分析:
这个是一道bfs题目,因为要能出去迷宫的话,那肯定是越快越好了,毕竟有岩浆在喷涌。。。
第一反应的思路是在bfs的每次循环中(!queue.empty()),第一步判断当前点是否已经到达了出口,如果是,返回true,否则继续。然后由于题目说了是先人走,后岩浆流动,也就是说如果人和岩浆同时到达某地,那人是可以走的。那么体现在代码上就应该是先对人进行四个方向的判断和入队操作,然后才是对岩浆的传播方向的判断和入队操作。
沿着这个思路,我先大致写了一下,发现问题不小。
人的走动好办,但是岩浆可就不好弄了。我对岩浆的处理是首先遍历迷宫,每遇到’!’就将它向四面流动传播(前提是可以)。但是这么做的话有一个问题。前面的某一个’!’点我对其进行传播后,会导致之后还未扫描到的某个点,由不是岩浆变成了岩浆,等到我扫描它的时候,程序又自动对它进行扩散。这意味着啥?这意味着我这边人刚刚走了一步,那么岩浆就走完了所有的步骤!因为事实上被原来是岩浆的格点所传播变成岩浆的那些格子,在本轮循环中,是不会继续传播的!……好蛋疼。

其实写到这儿就应该有所警觉了。我对两个参考对象分别进行了四个方向的探索,那这个题应该是个啥?
没错,双重bfs。

首先对岩浆用一个bfs,生成岩浆所能到达的所有地方相应的时间。
然后对人用一个bfs,每次除了判断’#’和’.’外,还要判断到达时间是比岩浆早还是晚,比岩浆早,那这个点就可以走,否则就不可以走。

这里有一个问题,一个岩浆向四面传播后,之后的扩散将由子岩浆来完成,也就是说一个岩浆将会且只会扩散传播一次,且传播顺序是按照由父到子的顺序来的,这是啥?没错,队列。也就是第一次的bfs。
先将所有’!’进队,这些全都是初始岩浆。然后开始像四个方向传播,每次成功传播就进队,这么一趟下来,所有进行传播的都是初始岩浆,且初始岩浆也全部出队,剩下的都是子岩浆,然后重复……得到了所有的岩浆可到达点,相应随手保存下到达次序就好。
人的bfs就很常规了,不再赘述,注意判断和岩浆的到达先后。

参考代码:

/**
 * name: Escape
 * P_ID: BNUOJ 49278
 * date:2014-04-05
 */
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

int dir[4][2] = {{1,0}, {-1,0}, {0,-1}, {0,1}};
char str[1005][1005];
bool vis[1005][1005];
int dis[1005][1005];
int N, M;
int x, y;
int ex, ey;
const int inf = 0x3f3f3f3f;

struct maze {
    int x;
    int y;
    int t;
};

bool judge(maze now)
{
    int a = now.x;
    int b = now.y;
    if(a<0 || a>=N || b<0 || b>=M) return false;
    return true;
}

void bfs_hot()
{
    queue<maze> myHot;
    for(int i=0; i<N; ++i)
    {
        for(int j=0; j<M; ++j)
        {
            if(str[i][j]=='!')
            {
                dis[i][j] = 0;
                maze temp;
                temp.x = i;
                temp.y = j;
                temp.t = 0;
                myHot.push(temp);
            }
        }
    }

    while(!myHot.empty())
    {
        maze temp = myHot.front();
        myHot.pop();
        for(int i=0; i<4; ++i)
        {
            maze next;
            next.x = temp.x + dir[i][0];
            next.y = temp.y + dir[i][1];
            next.t = temp.t + 1;
            if(judge(next) && next.t<dis[next.x][next.y])
            {
                dis[next.x][next.y] = next.t;
                myHot.push(next);
            }
        }
    }
}

bool bfs(int x, int y)
{
    queue<maze> myPath;
    maze first, next;
    first.x = x;
    first.y = y;
    first.t = 0;
    myPath.push(first);
    while(!myPath.empty())
    {
        maze temp = myPath.front();
        myPath.pop();
        if(temp.x==ex && temp.y==ey) return true;
        for(int i=0; i<4; ++i)
        {
            next.x = temp.x + dir[i][0];
            next.y = temp.y + dir[i][1];
            next.t = temp.t + 1;
            if(judge(next) && next.x==ex && next.y==ey && (next.t<=dis[next.x][next.y]))//终点处就算同时到达也算作Yes
                return true;
            if(!vis[next.x][next.y] && judge(next) && next.t<dis[next.x][next.y])//中间过程
            {
                vis[next.x][next.y] = 1;
                myPath.push(next);
            }
        }
    }
    return false;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        memset(vis, 0, sizeof(vis));
        //配合之后的'#'变更,使得所有岩浆可到达点的dis初始都是inf
        memset(dis, inf, sizeof(dis));
        scanf("%d%d", &N, &M);
        for(int i=0; i<N; i++)
            scanf("%s", str[i]);

        for(int i=0; i<N; ++i)
            for(int j=0; j<M; j++)
            {
                if(str[i][j]=='S')
                {
                    x = i;
                    y = j;
                }
                //用来标记,之后传播岩浆遇到'#'会受阻
                if(str[i][j]=='#')
                {
                    dis[i][j] = 0;
                }
                if(str[i][j]=='E')
                {
                    ex = i;
                    ey = j;
                }
            }
        bfs_hot();
        if(bfs(x, y)) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值