题目描述
哎,又是银首,要是你这个签到题少WA一发就金了
牛牛战队的队员打完比赛以后又到了日常甩锅的时间。他们心情悲伤,吃完晚饭以后,大家相约到一个街机厅去solo。牛牛和牛能进入了一个迷宫,这个迷宫里除了墙壁的阻拦,还会有僵尸的阻拦。情况十分复杂,牛能为了更快的追逐牛牛,迅速放出了大招,让牛牛原地眩晕,而眩晕的解药,也只有牛能自己拥有。
这一个迷宫可以简化为一个n行m列的矩阵,其中有一些僵尸,这些僵尸会在一个 的矩形中来回游走。他不会攻击眩晕状态下的人,只会攻击和他抢地盘的人。这名队员每次移动需要一个单位时间,而且他不能穿墙,不能和僵尸处于同一位置,在追到另一名队员之前也不能停下来。
在这一场追逐战中,要么牛能追逐成功,取得胜利,要么被僵尸击败,牛牛胜利。那谁会是最终的王者呢?
输入描述:
输入数据有很多行。第一行输入 个整数 ,分别表示迷宫的行、列,僵尸的数量,僵尸来回走动的长度。
第 行到第 行输入一个矩阵,每行输入一个字符串,第ii个字符串的第jj个字符表示矩阵中第 行 列的状态,如果字符是#表示是可以走的路,如果是&表示是障碍物,A是被眩晕队员的位置,L是追赶者的位置。
第 行到第 行每行输入两个整数 和一个字符串,第i行的数据表示第ii个僵尸当前时间会从第 行 列出发,沿着固有的方向前进 个单位时间后折返,再走回它之前的位置,再折返,依照这种方法循环下去。第三个字符串表示僵尸初始行进的方向,UP表示向上走,LEFT表示向左走,DOWN表示向下走,RIGHT表示向右走。数据保证在 个长度内僵尸不会碰到边界或者墙壁。
输出描述:
如果牛能可以追上牛牛,输出一个整数,表示最短追上的时间。否则输出一行Oh no。
输入
3 3 1 3
&&A
###
&&L
2 1 RIGHT
输出
2
扫描二维码关注公众号,回复: 9151371 查看本文章
说明
如果用*代表僵尸的位置,则起始时状态为
&&A
*##
&&L
一个单位时间后,牛能上走,僵尸向右走,两者并没有碰上
&&A
#*L
&&#
再一个单位时间后,牛能向上走,追上牛牛,取得胜利。
&&L
##*
&&#
输入
4 4 1 2
L#&A
##&#
#&##
####
3 3 DOWN
输出
Oh no
说明
初始状态
L#&A
##&#
#&*#
一单位时间后
##&A
L#&#
#&##
##*#
二单位时间后
##&A
##&#
L&*#
####
三单位时间后
##&A
##&#
#&##
L#*#
四单位时间后
##&A
##&#
#&*#
#L##
牛能如果再向左走的话就会跟僵尸碰个正着,而且不论牛能怎么往回走,在(4,3)总能遇见僵尸,所以他失败了。
题解
- 如果不考虑僵尸,这是一道很简单的BFS搜索
- 注意题目的条件
- 僵尸有统一的步伐
- 僵尸移动过程中不会遇到墙面提前返回
- 有了这俩条件,题目就变得简单多了
- 我们思考,既然都有统一的步伐,那么也就是说,对于某一个僵尸 在 位置,初始向下移动,行走长度为 ,那么我们知道,任意 的整数倍时间后,僵尸都回到 的初始位置。
- 同理,僵尸第一步向下走到达 的位置,那么再经过任意 整数倍后,僵尸还是在这里
- 一次类推,我们可以通过僵尸起始位置、行走长度、初始移动方向,得到这个僵尸在任意时间所到达的位置(对周期数取模)
- 但是我们思考,僵尸路径是徘徊的,也就是这样:
- 我们知道周期数是 (注意,这里是k-1的原因是:路径长度是 ,但是初始时刻占据了一个格子,不需要花费时间移动,需要时间移动到达的共有 个格子)
- (考虑每一次到达 位置)每 周期的时间,我们只是从 ,实际上我们到达 位置还可以从
- 也就是说,到达某一个点是两个方向的周期
- 现在,我们有了僵尸在任意时间到达的位置,那么我们可以用
(
手动滑稽)记录某一位置 在 时刻 有僵尸到达。 - 处理好了僵尸,我们来看追梦人(牛能)移动的方案
- 显然不能直接标记通过的位置,因为其实走过的路径是可以返回的。可能我们遇到了僵尸,因为不可以停止,那么可能我们在附近溜达一会,等僵尸走了,就可以通过了。因为存在这种状态,所以不可以直接标记。
- 我们来思考,移动的方案其实是完全取决于僵尸的,因为所有的僵尸共用一个周期,那么很容易想到,追梦人移动的方案其实就是为了躲避僵尸移动的周期。
- 那么按照僵尸的标记方式,标记追梦人移动过的位置即可。
- 其实这道题始终围绕着的都是僵尸的周期数,想明白这点就很好做了。
AC-Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const double eps = 1e-7;
int n, m, p, k;
int mod; // 周期数
string mp[505];
struct Node {
int x, y, t;
Node() { x = y = t = 0; }
Node(int x, int y, int t) :x(x), y(y), t(t) {}
};
int dx[] = { -1,1,0,0 }; // 依次:上下左右
int dy[] = { 0,0,-1,1 };
int vis[505][505][22]; // 避免和 僵尸周期 相同的 周期徘徊 状态
bool fuck[505][505][22]; // (i,j)位置在k时刻有僵尸
bool check(int x, int y, int t) {
if (x < 0 || x >= n) return false;
if (y < 0 || y >= m) return false;
if (mp[x][y] == '&') return false;
if (fuck[x][y][t % mod]) return false; // 没有僵尸
if (vis[x][y][t % mod]) return false; // 没走过
return true;
}
int bfs(Node start) {
queue<Node> q;
q.push(start);
vis[start.x][start.y][0] = true;
while (!q.empty()) {
Node now = q.front();
q.pop();
if (mp[now.x][now.y] == 'A')
return now.t;
for (int i = 0; i < 4; ++i) {
int nx = now.x + dx[i];
int ny = now.y + dy[i];
int nt = now.t + 1;
if (check(nx, ny, nt)) {
q.push(Node(nx, ny, nt));
vis[nx][ny][nt % mod] = true;
}
}
}
return 0;
}
void init() {
memset(fuck, false, sizeof fuck);
memset(vis, false, sizeof vis);
}
int main() {
while (cin >> n >> m >> p >> k) {
init();
mod = 2 * (k - 1);
Node st(0, 0, 0); // 起始点
for (int i = 0; i < n; ++i) {
cin >> mp[i];
for (int j = 0; j < m; ++j)
if (mp[i][j] == 'L')
st.x = i, st.y = j;
}
for (int i = 0; i < p; ++i) {
int x, y; cin >> x >> y; --x, --y; // 输入数据从1开始,代码从0开始
fuck[x][y][0] = true; // 记录僵尸起始位置
string s; cin >> s;
int dst = 0; // 记录僵尸移动方向
if (s[0] == 'U') dst = 0;
else if (s[0] == 'D') dst = 1;
else if (s[0] == 'L') dst = 2;
else if (s[0] == 'R') dst = 3;
for (int j = 1; j < k; ++j) {
x += dx[dst], y += dy[dst]; // 当前僵尸下一个位置
fuck[x][y][j % mod] = true; // 僵尸在第j秒会到达这个位置 正方向
fuck[x][y][mod - j] = true; // 僵尸在第j秒会到达这个位置 反方向
}
}
int ans = bfs(st);
if (ans) cout << ans << endl;
else cout << "Oh no" << endl;
}
}