刷题:噩梦

106 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

题目来源:hdu3085

题目大意:

给定一张 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。

输入样例:

3
5 6
XXXXXX
XZ..ZX
XXXXXX
M.G...
......
5 6
XXXXXX
XZZ..X
XXXXXX
M.....
..G...
10 10
..........
..X.......
..M.X...X.
X.........
.X..X.X.X.
.........X
..XX....X.
X....G...X
...ZX.X...
...Z..X..X

输出样例:

1
1
-1

算法分析:

双向广搜

       由于是男孩和女孩同时移动,而不是只有一个人移动,所以这题要用双向广搜。

  • 我们在bfs中按时间t从 1 开始枚举。如果男孩和女孩都不能再继续扩展,则跳出枚举。
  • 对于男孩,每次扩展三步,并标记扩展到的格子。如果某个能扩展的格子被女孩扩展过,那么直接返回现在的时间。
  • 对于女孩,每次扩展一步,并标记扩展到的格子。如果某个能扩展的格子被男孩扩展过,那么直接返回现在的时间。
  • 对于鬼,由于鬼是无视墙的,所以我们只需要在扩展男孩和女孩的时候,判断下有没有进入鬼的占领范围即可。题目中已经给出了,在第 k 秒后所有与鬼的曼哈顿距离不超过 2k 的位置都会被鬼占领我们在第 t 秒扩展的时候,判断被扩展的格子是否与某个鬼的曼哈顿距离小于 2t 即可。

时间复杂度

       每个格子最多只会被扩展一次(如果被扩展了两次,男孩和女孩就会合)一共有 T 组测试数据,每组测试数据的地图有 N×M 个格子,所以时间复杂度是 O(TNM)

AC代码:

#include <bits/stdc++.h>
using namespace std;

const short dx[4] = {0, 1, 0, -1};
const short dy[4] = {1, 0, -1, 0};
const short N = 805;

struct Point{short x, y, s;};
short n, m;
bool g[N][N];
short st[N][N];
Point ghost[2]; 

short sub(short a, short b){
    return a > b ? a - b : b - a;
}
bool check(short x, short y, short t){
    for (short i = 0; i < 2; i ++ )
        if (sub(ghost[i].x, x) + sub(ghost[i].y, y) <= t << 1)
            return false;
    return true; 
}
bool BFS(queue<Point> &q, short t, short v){
    while (q.size() && q.front().s < t * v)    {
        Point u = q.front(); q.pop();
        if (!check(u.x, u.y, t)) continue;
        for (short i = 0; i < 4; i ++ )        {
            short a = u.x + dx[i], b = u.y + dy[i]; 
            if (~a && ~b && a < n && b < m && g[a][b] && check(a, b, t))
                if (!st[a][b]){
                    st[a][b] = st[u.x][u.y];
                    q.push({a, b, u.s + 1});
                }
                else if (st[a][b] != st[u.x][u.y]) return true;
        }
    }
    return false;
}
short bfs(Point boy, Point girl){
    memset(st, 0, sizeof st); 
    queue<Point> q_boy, q_girl;
    q_boy.push(boy), q_girl.push(girl);
    st[boy.x][boy.y] = 1, st[girl.x][girl.y] = 2;
    for (short t = 1; q_boy.size() || q_girl.size(); t++)
        if (BFS(q_boy, t, 3) || BFS(q_girl, t, 1))
            return t;
    return -1;
}

int main(){
    short test;
    scanf("%hd", &test);
    while (test--){
        scanf("%hd%hd\n", &n, &m);
        Point boy, girl;
        for (short i = 0, cnt = 0; i < n; i ++ , getchar())
            for (short j = 0; j < m; j ++ ){
                char ch = getchar();
                if (ch == 'M') boy = {i, j, 0};
                else if (ch == 'G') girl = {i, j, 0};
                else if (ch == 'Z') ghost[cnt ++ ] = {i, j, 0};
                g[i][j] = ch != 'X';
            }
        printf("%d\n", bfs(boy, girl)); 
    }
    return 0;
}