跳到主要内容

BISHI80. 走迷宫

题目描述​

给定一个 n 行 m 列的网格。你从起点 s 出发,每一次可以向上、下、左、右 移动一步(不超出边界)。某些格子上存在障碍物,无法经过。 求从 s 移动到终点 t 的最少步数;若无法到达,输出 -1。

输入格式​

第一行输入两个整数 n、m,代表网格的行数与列数。 第二行输入四个整数 x1、y1、x2、y2,代表起点与终点的坐标(行列均从 1 开始)。 此后 n 行,第 i 行输入一个长度为 m 的字符串:* 表示障碍物,. 表示可通行。 保证起点所在格子可通行。

输出格式​

输出一个整数,表示最少移动次数;若无法到达,输出 -1。

示例​

示例 1​

输入:

5 5
1 1 5 5
.....
****.
.....
**.**
.....

输出:

12

说明: 第 2 行只有第 5 列可通行、第 4 行只有第 3 列可通行, 必须绕行,最短需要 12 步。

示例 2​

输入:

5 5
1 1 4 5
.....
****.
.....
**.**
.....

输出:

-1

说明: 终点 (4,5) 被障碍物隔开,无法到达。

示例 3​

输入:

5 5
1 1 5 5
.....
****.
.....
*****
.....

输出:

-1

说明: 第 4 行整行是障碍物,把上下完全隔断。


解题思路​

第一步:理解问题本质​

网格里每一步的代价都是 1,这是典型的无权图最短路。 无权图最短路的标准算法就是 BFS —— 按层扩散,第一次到达某点的层数 就是它到起点的最短距离。

第二步:用 DFS 找最短路(不推荐)​

DFS 也能枚举所有路径并取最小值,但需要遍历全部路径, 复杂度会退化到指数级,且要额外维护最小值,容易出错。

第三步:BFS(推荐)​

  • 用 dist 数组同时承担两个职责:是否访问过(-1 表示没访问) 和到起点的距离。
  • 起点 dist[sx][sy] = 0 并入队。
  • 每次出队,向四方向扩展:若下一格在界内、可通行、且 dist 仍为 -1, 则 dist[下一格] = dist[当前] + 1 并入队。
  • 最后输出 dist[tx][ty]:若终点从未被访问,它天然是 -1, 正好就是题目要求的「无法到达」。

把「是否访问」和「距离」合并进一个数组,是网格 BFS 的常见小技巧。


完整代码实现​

算法可视化 · 逐帧回放这道题的代码280 帧 · 录制输入
import sys
from collections import deque


def solve() -> None:
"""BFS 求网格最短步数"""
data = sys.stdin.read().split()
if not data:
return
idx = 0
n = int(data[idx]); m = int(data[idx + 1]); idx += 2
x1 = int(data[idx]); y1 = int(data[idx + 1])
x2 = int(data[idx + 2]); y2 = int(data[idx + 3]); idx += 4
grid = data[idx:idx + n]

sx, sy = x1 - 1, y1 - 1 # 题目坐标从 1 开始,转成 0 开始
tx, ty = x2 - 1, y2 - 1

dist = [[-1] * m for _ in range(n)]
dist[sx][sy] = 0
queue = deque([(sx, sy)])
while queue:
x, y = queue.popleft()
if x == tx and y == ty:
break
for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < m and dist[nx][ny] == -1 and grid[nx][ny] == '.':
dist[nx][ny] = dist[x][y] + 1
queue.append((nx, ny))
sys.stdout.write(str(dist[tx][ty]) + "\n")


if __name__ == "__main__":
solve()

示例推演​

以示例 1 的网格(1 起坐标)为例:

行1: . . . . .
行2: * * * * .
行3: . . . . .
行4: * * . * *
行5: . . . . .

因为行 2 只有第 5 列通、行 4 只有第 3 列通,路径被「挤」成:

段路径步数
行1 向右到第 5 列(1,1)→…→(1,5)4
下行经过行2、行3 的第 5 列(2,5)1
行3 内向左到第 3 列(3,4)→(3,3)2
下行到行4、行5 第 3 列(4,3)→(5,3)2
行5 向右到第 5 列(5,4)→(5,5)2
其余(3,5) 是行2→行3 的一步1

合计 4 + 1 + 1 + 2 + 2 + 2 = 12,与答案一致。


复杂度分析​

解法时间复杂度空间复杂度说明
DFS 枚举所有路径指数级O(n*m)不推荐
BFSO(n*m)O(n*m)推荐

易错点总结​

1. 坐标从 1 开始​

输入坐标是行列都从 1 计数,读入后一定要 -1 转成 0 起点, 否则会整体偏移一行一列。

2. dist 初值用 -1 而不是 0​

如果初值设成 0,「没访问」和「距离为 0」就分不清了。 用 -1 表示未访问,顺便让「不可达」直接输出 -1。

3. 障碍物符号是 * 不是 #​

本题用 * 表示障碍,判断时别习惯性写成 #。


扩展思考​

  • 如果每次移动代价不同(有的格子走起来更贵),就换成 Dijkstra。
  • 多源最短路(多个起点)只需在初始化时把所有起点都入队。

相关题目​

自测​

加载评论中...