HDU3085_Nightmare Ⅱ_双向BFS

题意

在一个迷宫里,有一个boy和一个girl,还有两个鬼。boy每秒钟走 3 步,girl 每秒钟走 1 步,鬼每秒钟超两步内能到达的格内分身。注意,鬼可以无视墙壁。并且, 每一秒,鬼分裂boy和girl再走。如果boy或girl和鬼在一个格子里,它们会被吃掉。问在被吃掉之前boy和girl能否相遇。

思路

一开始想的是 预处理+BFS。就是先把 鬼 和 boy 到达每个点的时间求出来,然后对 girl BFS。果不其然,TLE。这个题是 双向BFS。

1.从 boy 和 girl 两个点出发,双向BFS。
2.不需要对 鬼 进行预处理,只需在到达每个点是判断点到鬼的距离即可,O(1)。
3.需要注意的一点是,输入的时候如果一个字符一个字符地读会TLE,一行一行地读就AC了。

双向DFS

其实双向dfs就是把 队列 和 dis 数组都搞成二维的,对应每一方向。然后把起点和终点分别放进一个队列里,当两个队列出现重合点时,搜索就结束了。
双向DFS较单向有很大幅度的效率提升。

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=3085

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>

using namespace std;

typedef pair<int, int> P;

const int maxn = 810;
const int  inf = 0x3f3f3f3f;

const int mx[4] = {-1, 0, 1, 0};
const int my[4] = {0, 1, 0, -1};

int cas;
int n, m;
char G[maxn][maxn];
P M, W, Z0, Z1;
bool dis[2][maxn][maxn];
queue<P> qu[2];

int Abs(int x)
{
    return x > 0 ? x : -x;
}

//检查点是不是安全
bool check(int x, int y, int steps)
{
    if(x < 0 || x >= n || y < 0 || y >= m) return false;
    if(G[x][y] == 'X') return false;

    //和鬼的距离
    if(Abs(x - Z0.first) + Abs(y - Z0.second) <= 2 * steps) return false;
    if(Abs(x - Z1.first) + Abs(y - Z1.second) <= 2 * steps) return false;

    return true;
}

//u 是队列序号,steps 是时间
bool bfs(int u, int steps)
{
    int num = qu[u].size();//这一点学到了

    while(num --)
    {
        int x = qu[u].front().first, y = qu[u].front().second;
        qu[u].pop();

        if(!check(x, y, steps)) continue;//因为鬼先走,所以这里还要检查一次

        for(int i= 0; i< 4; i++)
        {
            int xx = x + mx[i], yy = y + my[i];

            if(!dis[u][xx][yy] && check(xx, yy, steps))
            {
                if(dis[u^1][xx][yy]) return true;//会师成功

                dis[u][xx][yy] = true;
                qu[u].push(P(xx, yy));
            }
        }
    }

    return false;
}

int bid_bfs()
{
    //初始化
    memset(dis, false, sizeof dis);
    while(qu[0].size()) qu[0].pop();
    while(qu[1].size()) qu[1].pop();

    //两个起点各自入队
    dis[0][M.first][M.second] = true;
    qu[0].push(M);
    dis[1][W.first][W.second] = true;
    qu[1].push(W);

    //boy 走三步,girl 走一步
    for(int i= 1; qu[0].size() || qu[1].size(); i++)
    {
        if(bfs(0, i)) return i;
        if(bfs(0, i)) return i;
        if(bfs(0, i)) return i;
        if(bfs(1, i)) return i;
    }

    //不能会师
    return -1;
}

int main()
{
    cin >> cas;
    while(cas --)
    {
        scanf("%d %d", &n, &m);

        for(int i= 0; i< n; i++)
            scanf(" %s", &G[i]);

        Z0.first = -1;
        for(int i= 0; i< n; i++)
            for(int j= 0; j< m; j++)
        {
            if(G[i][j] == 'M') M = P(i, j);
            if(G[i][j] == 'G') W = P(i, j);
            if(G[i][j] == 'Z')
                if(Z0.first == -1) Z0 = P(i, j);
                else Z1 = P(i, j);
        }

        cout << bid_bfs() << endl;
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值