题解 - 幻象迷宫
题目
幻象迷宫可以认为是无限大的,不过它由若干个 \(N\times M\) 的矩阵重复组成。矩阵中有的地方是道路,用 \(\verb!.!\) 表示;有的地方是墙,用 \(\verb!#!\) 表示。起始位置用 \(\verb!S!\) 表示。也就是对于迷宫中的一个点 \((x,y)\),如果 \((x \bmod n,y \bmod m)\) 是 \(\verb!.!\) 或者 \(\verb!S!\),那么这个地方是道路;如果 \((x \bmod n,y \bmod m)\) 是 \(\verb!#!\),那么这个地方是墙。你可以向上下左右四个方向移动,当然不能移动到墙上。
你能否走出幻象迷宫?
思路简析
一个显而易见的想法是如果能从起点出发再能到一个边缘且该边缘的对面还有路能通向起点就是成功。
hack1:
S.#..
#####
#...#
一个更强力的 hack2:
.#.
###
.#S
正确的思路是记录两组 \((x, y)\),一组是经过取模的,另一组是没有取过模的。显然如果又经过了一个点,而未取模的点对与上次经过时不同那么显然你已经绕了一圈了,此时是 Yes
;但是如果你不能到无限远,那说明你只能到有限个格,dfs
不会 RE。
这个代码细节比较多。
代码实现
Time complexity:\(O(n\cdot m)\)
#include<bits/stdc++.h>
using namespace std;
namespace {
#define filein(x) freopen(x".in", "r", stdin)
#define fileout(x) freopen(x".out", "w", stdout)
#define files(x) filein(x), fileout(x)
using namespace std;
#define ll long long
#define db double
const int man = 1.5e3+10;
const int dis[5][3] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
}
int n, m, X, Y, f;
int mp[man][man], vis[man][man][3];
void dfs (int, int, int, int) ;
int main () {
#ifndef ONLINE_JUDGE
files("test");
#endif
while (scanf("%d%d", &n, &m) != EOF) {
f = 0;
for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) {
char c;
scanf(" %c ", &c);
mp[i][j] = (c!='#');
vis[i][j][0] = vis[i][j][1] = vis[i][j][2] = 0;
if (c == 'S') X = i, Y = j;
} dfs(X, Y, X, Y);
puts(f? "Yes": "No");
} return 0;
}
// ---
void dfs (int x, int y, int orx, int ory) {
// printf("%d %d %d %d\n", x, y, orx, ory);
if (vis[x][y][0]) return f = (vis[x][y][1]!=orx||vis[x][y][2]!=ory), void();
vis[x][y][0] = 1, vis[x][y][1] = orx, vis[x][y][2] = ory;
// printf("%d %d %d\n", x, y, vis[x][y][0]);
for (int i = 0; i < 4; ++ i) {
int gx = x+dis[i][0], gy = y+dis[i][1];
gx = (gx+n-1)%n+1, gy = (gy+m-1)%m+1;
// printf("%d %d\n", gx, gy);
if (mp[gx][gy]) dfs(gx, gy, orx+dis[i][0], ory+dis[i][1]);
if (f) break; //attention, it shouid be broken there, becuse f is changed as (vis[x][y][1]!=orx||vis[x][y][2]!=ory).
} return void();
}