Nightmare Ⅱ(BFS)

题目连接: Nightmare Ⅱ

题目:

Last night, little erriyue had a horrible nightmare. He dreamed that he and his girl friend were trapped in a big maze separately. More terribly, there are two ghosts in the maze. They will kill the people. Now little erriyue wants to know if he could find his girl friend before the ghosts find them.
You may suppose that little erriyue and his girl friend can move in 4 directions. In each second, little erriyue can move 3 steps and his girl friend can move 1 step. The ghosts are evil, every second they will divide into several parts to occupy the grids within 2 steps to them until they occupy the whole maze. You can suppose that at every second the ghosts divide firstly then the little erriyue and his girl friend start to move, and if little erriyue or his girl friend arrive at a grid with a ghost, they will die.
Note: the new ghosts also can devide as the original ghost.

Input
The input starts with an integer T, means the number of test cases.
Each test case starts with a line contains two integers n and m, means the size of the maze. (1<n, m<800)
The next n lines describe the maze. Each line contains m characters. The characters may be:
‘.’ denotes an empty place, all can walk on.
‘X’ denotes a wall, only people can’t walk on.
‘M’ denotes little erriyue
‘G’ denotes the girl friend.
‘Z’ denotes the ghosts.
It is guaranteed that will contain exactly one letter M, one letter G and two letters Z.

Output
Output a single integer S in one line, denotes erriyue and his girlfriend will meet in the minimum time S if they can meet successfully, or output -1 denotes they failed to meet.

Sample Input
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

Sample Output
1
1
-1

解题思路:

这个题有些复杂, 起初一想 两个人和鬼都应该BFS跑, 不出所料吃了T.
但实际上, 需要BFS的只有两个人, 因为鬼是可以穿墙的, 哪里都能跑, 因此鬼就相当于每轮向外扩两圈. 这样的话我们就能略去鬼需要的两次BFS.
而在判断人是否能跑到某个点时, 我们只需要判断鬼在当前轮次i*2的步数下能否到达该点即可.

AC代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
char a[1000][1000]; int n, m, res;
bool vis[1000][1000][2]; //分别记录M和G是否到达过该位置
int gx[2], gy[2]; //存放两个鬼的初始位置坐标
int fx[][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
struct node {
	int x, y;
};
queue<node> q[2]; //M, G
bool judge(int dx, int dy) //判断M和G在该位置能否存在.
{
	if (dx<1 || dy<1 || dx>n || dy>m || a[dx][dy] == 'X') return 0;
	if (abs(dx - gx[0]) + abs(dy - gy[0]) <= 2 * res) return 0; //鬼1能否达到
	if (abs(dx - gx[1]) + abs(dy - gy[1]) <= 2 * res) return 0; //鬼2能否达到
	return 1;
}
bool bfs(int ind) //ind是为了判断是M还是G
{
	int t = q[ind].size(); //只跑当前轮次
	while (t--) {
		node op = q[ind].front(); q[ind].pop();
		if (!judge(op.x, op.y)) continue; //因为鬼先手, 所以上一轮能跑到的地方这一轮不一定能存活.
		for (int i = 0; i < 4; i++) {
			int dx = op.x + fx[i][0];
			int dy = op.y + fx[i][1];
			if (vis[dx][dy][ind] || !judge(dx, dy)) continue;
			if (vis[dx][dy][ind ^ 1]) return 1; //相遇
			vis[dx][dy][ind] = 1; q[ind].push({ dx, dy });
		}
	}
	return 0;
}
int fact()
{
	res = 0;
	while (!q[0].empty() && !q[1].empty()) { //如果M或者G有1个为空, 则无法相遇
		res++;
		int cou = 3; while (cou--) if (bfs(0)) return res;
		if (bfs(1)) return res;
	}
	return -1; //无法相遇返回-1
}
int main(void)
{
	int t; cin >> t;
	while (t--) {
		while (!q[0].empty()) q[0].pop(); while (!q[1].empty()) q[1].pop(); memset(vis, 0, sizeof(vis));
		scanf("%d %d", &n, &m); int gin = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%s", (a[i] + 1));
			for (int j = 1; j <= m; j++) {
				if (a[i][j] == 'M') { q[0].push({ i, j }); vis[i][j][0] = 1; } //M
				if (a[i][j] == 'G') { q[1].push({ i, j }); vis[i][j][1] = 1; } //G
				if (a[i][j] == 'Z') { gx[gin] = i, gy[gin++] = j; }
			}
		}
		cout << fact() << endl;
	}
	return 0;
}

特别说明: 由于本题测试数据问题, 如果你读图采用%c的方式读, 可能会有问题. 在样例输入时, 第二个图和第三个图之间多了一空行是题目自带的, 因此有可能在提示我们, 一个个测试样例之间可能会含有不等量的回车, 因此请使用%s的方式读入.

END

发布了20 篇原创文章 · 获赞 0 · 访问量 512

猜你喜欢

转载自blog.csdn.net/weixin_45799835/article/details/104291711