P1363 幻象迷宫
bfs, dfs, stack, https://www.luogu.com.cn/problem/P1363
题目背景
(喵星人 LHX 和 WD 同心协力击退了汪星人的入侵,不幸的是,汪星人撤退之前给它们制造了一片幻象迷宫。)
WD:呜呜,肿么办啊……
LHX:momo...我们一定能走出去的!
WD:嗯,+U+U!
题目描述
幻象迷宫可以认为是无限大的,不过它由若干个
请你告诉 LHX 和 WD,它们能否走出幻象迷宫(如果它们能走到距离起点无限远处,就认为能走出去)。如果不能的话,LHX 就只好启动城堡的毁灭程序了……当然不到万不得已,他不想这么做。
输入格式
输入包含多组数据。
每组数据的第一行是两个整数
接下来是一个
输出格式
对于每组数据,输出一个字符串,Yes 或者 No。
输入输出样例 #1
输入 #1
5 4
##.#
##S#
#..#
#.##
#..#
5 4
##.#
##S#
#..#
..#.
#.##输出 #1
Yes
No说明/提示
- 对于
的数据, ; - 对于
的数据, ; - 对于
的数据, ,每个测试点不超过 组数据。
这是一个经典的地牢/迷宫穿梭问题。由于迷宫在四个方向上无限循环延伸,我们要判断能否走到无限远处。
解题思路
核心判定条件: 如果我们能从起点出发,到达某一个网格副本中的位置
,并且也能到达另一个网格副本中的位置 ,满足它们在单个迷宫矩阵中的相对位置相同(即 且 ),但它们的实际坐标不同(即 ),这就意味着我们找到了一条跨越边界的环路。 一旦存在这样的环路,我们就可以沿着这个环路无限走下去,从而到达距离起点无限远的地方。 具体实现:
- 使用深度优先搜索(DFS)或广度优先搜索(BFS)遍历迷宫。由于 Python 的递归深度限制,使用非递归(迭代式)的 DFS 会更加安全和高效。
- 维护一个
visited数组记录在模和模 意义下的位置是否被访问过。 - 维护两个数组
rec_x和rec_y,记录第一次到达该相对位置时的真实坐标。 - 在搜索过程中:
- 如果遇到一个从未到达过的相对位置
,记录其真实坐标并继续搜索。 - 如果遇到一个已经到达过的相对位置
,比较当前的真实坐标 与记录的坐标。如果不同,说明找到了可行通路,直接输出 Yes;如果相同,则无需重复搜索。 - 如果所有可达点都搜索完毕仍未触发上述条件,说明活动范围是有界的,输出
No。
- 如果遇到一个从未到达过的相对位置
Python 3 实现代码
import sys
def solve():
# 提高输入读取效率
input_data = sys.stdin.read().split()
if not input_data:
return
idx = 0
num_inputs = len(input_data)
# 方向数组:上下左右
dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)]
while idx < num_inputs:
N = int(input_data[idx])
M = int(input_data[idx + 1])
idx += 2
grid = []
start_x, start_y = -1, -1
for r in range(N):
row = input_data[idx]
grid.append(row)
if start_x == -1:
c = row.find("S")
if c != -1:
start_x, start_y = r, c
idx += 1
# 使用一维数组扁平化存储,提高 Python 的运行效率
size = N * M
visited = [False] * size
rec_x = [0] * size
rec_y = [0] * size
# 初始状态
start_flat = start_x * M + start_y
visited[start_flat] = True
rec_x[start_flat] = start_x
rec_y[start_flat] = start_y
stack = [(start_x, start_y)]
possible = False
while stack:
cx, cy = stack.pop()
for dx, dy in dirs:
nx, ny = cx + dx, cy + dy
rx, ry = nx % N, ny % M
# 墙壁不可通行
if grid[rx][ry] == "#":
continue
flat = rx * M + ry
if visited[flat]:
# 如果相对位置相同但实际坐标不同,说明找到了无限延伸的环路
if rec_x[flat] != nx or rec_y[flat] != ny:
possible = True
break
else:
visited[flat] = True
rec_x[flat] = nx
rec_y[flat] = ny
stack.append((nx, ny))
if possible:
break
if possible:
print("Yes")
else:
print("No")
if __name__ == "__main__":
solve()复杂度分析
- 时间复杂度:每个测试点最多包含 10 组数据。对于每组数据,在最坏情况下,我们最多会遍历到原迷宫大小的所有状态(因为一旦重复访问且坐标不同就会立即退出)。状态总数为
。由于 ,单组数据的最大状态数为 。使用一维数组存储和迭代式 DFS,可以在时限内完成。 - 空间复杂度:
,用于存储访问状态、真实坐标和栈。