📅  最后修改于: 2023-12-03 15:42:22.218000             🧑  作者: Mango
本题为"门"问题,属于图论中的最短路问题。给定一个 $n\times m$ 的迷宫地图,地图中的每个位置可能是墙(用 "#" 表示),也可能是平原(用 "." 表示),还可能是门(用 "S" 和 "E" 表示)。
地图中存在多个门,每个门的编号为 $1$ 至 $9$。其中,门的编号一定是成对出现的,即如果存在 $i$ 号门,则一定存在 $i+9$ 号门。对于每一对相同编号的门,它们之间会存在一些单向门,即只能从一个门进入,而无法从另一个门出去。两个门之间如果有多个单向门,我们认为它们是相互可达的。
现在要求你从某一个门出发,依次经过一些单向门,到达另一个门(编号与起点不同),问最少需要经过多少个单向门。
本题的解法是使用 BFS 算法,每次将当前节点的所有可到达节点加入队列中。
我们需要记录每个门对应的编号和出发门的编号。
对于每个位置,可能有三种情况:可达、不可达或者是另一个门,我们用三个数组 $able$、$unable$ 和 $door$ 分别保存这三种情况。
由于每个节点只会被遍历一次,所以时间复杂度为 $O(nm)$,空间复杂度为 $O(nm)$。
以下是 python 代码实现:
# -*- coding: utf-8 -*-
from collections import deque
# 输入地图信息
n, m = map(int, input().split())
maze = [list(input()) for _ in range(n)]
# 初始化数组
able = [[False] * m for _ in range(n)] # 可达
unable = [[False] * m for _ in range(n)] # 不可达
door = [[0] * m for _ in range(n)] # 门编号
start = None
# 记录每一个门对应的编号
doors = [[] for _ in range(10)]
# 处理地图
for i in range(n):
for j in range(m):
if maze[i][j] == "S":
start = (i, j) # 记录起始点
elif maze[i][j] in "123456789":
door[i][j] = int(maze[i][j])
doors[int(maze[i][j])].append((i, j))
elif maze[i][j] == ".":
able[i][j] = True
# 记录最少需要经过的门数
ans = float("inf")
# BFS 遍历
for s in doors[door[start[0]][start[1]]]: # 从同一个门编号的门中开始遍历
visited = [[False] * m for _ in range(n)]
visited[s[0]][s[1]] = True
q = deque([(s, 0)])
while q:
pos, step = q.popleft()
if maze[pos[0]][pos[1]] != "E" and door[pos[0]][pos[1]] != 0 and door[pos[0]][pos[1]] != door[start[0]][start[1]]:
# 如果走到终点且到该终点的门的编号和起始门的编号不一样,则更新最小步数
ans = min(ans, step)
break
for dx, dy in [(-1, 0), (0, -1), (1, 0), (0, 1)]:
nx, ny = pos[0] + dx, pos[1] + dy
if 0 <= nx < n and 0 <= ny < m and not visited[nx][ny]:
if able[nx][ny] or (door[nx][ny] != 0 and door[nx][ny] == door[pos[0]][pos[1]]):
visited[nx][ny] = True
q.append(((nx, ny), step + 1))
print(ans)
以上就是本题的解题思路和算法实现。