噩梦(双向BFS)

Posted 海边微风起

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了噩梦(双向BFS)相关的知识,希望对你有一定的参考价值。

给定一张N*M的地图,地图中有1个男孩,1个女孩和2个鬼。

字符“.”表示道路,字符“X”表示墙,字符“M”表示男孩的位置,字符“G”表示女孩的位置,字符“Z”表示鬼的位置。

男孩每秒可以移动3个单位距离,女孩每秒可以移动1个单位距离,男孩和女孩只能朝上下左右四个方向移动。

每个鬼占据的区域每秒可以向四周扩张2个单位距离,并且无视墙的阻挡,也就是在第k秒后所有与鬼的曼哈顿距离不超过2k的位置都会被鬼占领。

注意: 每一秒鬼会先扩展,扩展完毕后男孩和女孩才可以移动。

求在不进入鬼的占领区的前提下,男孩和女孩能否会合,若能会合,求出最短会合时间。

输入格式

第一行包含整数T,表示共有T组测试用例。

每组测试用例第一行包含两个整数N和M,表示地图的尺寸。

接下来N行每行M个字符,用来描绘整张地图的状况。(注意:地图中一定有且仅有1个男孩,1个女孩和2个鬼)

输出格式

每个测试用例输出一个整数S,表示最短会合时间。

如果无法会合则输出-1。

每个结果占一行。


emmmmm, 当时就打了个测试程序就回班了, 回来之后看到我的代码里有这个东西:

 

 啊啊啊, 孟神, 你的算法进阶真不是我拿的啊。
进入正题--

求步数的话我们很容易想到BFS, 但这是两个人, 其实也很容易想到双向BFS, 创建两个队列, 在合法的状态下同时搜索, 当一个人搜索到另一个点人访问过的点时, 此时的步数就是最少步数;

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e5 + 100;
const int MAXM = 3e3 + 10;

template < typename T > inline void read(T &x) {
    x = 0; T ff = 1, ch = getchar();
    while(!isdigit(ch)) {
        if(ch == \'-\') ff = -1;
        ch = getchar();
    } 
    while(isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    } 
    x *=ff;
} 

template < typename T > inline void write(T x) {
    if(x < 0) putchar(\'-\'), x = -x;
    if(x > 9) write(x / 10); 
    putchar(x % 10 + \'0\'); 
} 

int T, n, m;
int vis[MAXM][MAXM];
int dx[5] = {1, 0, -1, 0};
int dy[5] = {0, 1, 0, -1};
char ch[MAXM][MAXM];  
pair < int, int > boy, girl,  ghost[2];

// 梁神我的算法进阶是不是在你那 -- msm 

inline bool check(int xx, int yy, int dis) {
    if(xx < 0 || xx >= n || yy < 0 || yy >= m || ch[xx][yy] == \'X\') return false;
    for(int i = 0; i < 2; ++i) {
        if(abs(xx - ghost[i].first) + abs(yy - ghost[i].second) <= 2 * dis) return false;
    }
    return true;
}

inline int BFS() {
    memset(vis, 0, sizeof(vis));
    queue < pair < int, int > > qb, qg;
    qb.push(boy);
    qg.push(girl);
    int dis = 0;
    while(!qb.empty() || !qg.empty()) {
        ++dis;
        for(int i = 0; i < 3; ++i) {
            int len = qb.size();
            for(int j = 0; j < len; ++j) {
                pair < int, int > x;
                x = qb.front();
                qb.pop();
                int a = x.first, b = x.second;
                if(!check(a, b, dis)) continue;
                for(int k = 0; k < 4; ++k) {
                    int u = a + dx[k], v = b + dy[k];
                    if(check(u, v, dis)) {
                        if(vis[u][v] == 2) return dis;
                        if(!vis[u][v]) {
                            vis[u][v] = 1;
                            qb.push({u, v});
                        }
                    }
                }
            }
        }
        int len = qg.size(); 
        for(int i = 0; i < len; ++i) {
            pair < int, int > x; 
            x = qg.front(); 
            qg.pop(); 
            int a = x.first, b = x.second; 
            if(!check(a, b, dis)) continue; 
            for(int k = 0; k < 4; ++k) { 
                int u = a + dx[k], v = b + dy[k]; 
                if(check(u, v, dis)) {
                    if(vis[u][v] == 1) return dis;
                    if(!vis[u][v]) {
                        vis[u][v] = 2;
                        qg.push({u, v});
                    }
                }
            }
        }
        
    }
    return -1;
}

int main() {
    read(T);
    while(T--) {
        read(n); read(m);
        for(int i = 0; i < n; ++i) {
            scanf("%s", ch[i]);
        }
        int tot = 0;
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < m; ++j) {
                if(ch[i][j] == \'M\') boy = {i, j};
                else if(ch[i][j] == \'G\') girl = {i, j};
                else if(ch[i][j] == \'Z\') ghost[tot++] = {i ,j};
            }
        }
        write(BFS());
        puts("");
    } 
    return 0;
} 

 

以上是关于噩梦(双向BFS)的主要内容,如果未能解决你的问题,请参考以下文章

HDU3085nightmare2 双向BFS

Knight Moves (双向bfs)

UVa1601 The Morning after Halloween(双向bfs)

双向bfs和双向dfs

我们啥时候应该使用普通 BFS 而不是双向 BFS?

POJ1915Knight Moves(单向BFS + 双向BFS)