📅  最后修改于: 2023-12-03 15:42:13.298000             🧑  作者: Mango
本题是 “门” 节点的一道典型题目,出自 GATE CS Mock 2018 的设置 2 ,第 58 题。
一个无向图中,每个节点要么是门 (G),要么是墙 (W)。其中,一些门被标记为入口(S),另一些门被标记为出口(E)。你一开始在某个入口门口,你要去找到一个出口。你可以通过走到相邻的门或墙,但你不能穿过墙。请找到从起点到出口的最短路径长度。
例如,记号 “S” 即表示起点, “E” 即表示终点:
S W G W
G W E W
W W G W
W W W W
从上例中的起点,我们最短路径经过 3 个门,长度为 3。
输入: 输入的第一行包含一个整数 T,表示测试用例的数量。随后是针对每个测试用例的输入。每个测试用例的输入的第一行包含一个整数 N,表示图形中节点的数量。下一行包含 N 个字符(G 或 W),描述了每个节点的类型。下一行包含一个整数 M,表示图形中门的数量。下一行包含 M 个整数 x1, y1, x2, y2, ......, xm, ym,表示每个门的位置。下一行包含 N 个整数,它们是源节点(S)到该位置的最短路径的长度,它们可以是 -1(如果无法从起点达到该节点) 最小整数值。同样的,下一行有 N 个整数,表示每个位置到目标节点(E)的最短路径长度。它们的含义也可以是 -1(如果从该位置无法到达目标节点)。两个相邻的整数之间都有一个空格。
输出: 对于每个测试用例,请打印从起点到出口的最短路径长度。
输入:
2
13
W W W W W W W W W W W W W
3
3 3 7 3 11 3
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1
12
W W W W W W W W W W W W
2
0 0 11 0
4 4 4 4 4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3 3 3 3 3
8 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0
输出:
3
6
这是一道经典的广度优先搜索(BFS)问题,需要求从起点到终点的最短路径。具体思路如下:
将起点加入队列中,标记已访问。
不断从队列中读取节点,并考虑其所有相邻的节点,如果其尚未标记已访问,则加入队列中。
如果目标节点出现在队列中,则算法结束并返回路径长度。
在这一过程中,我们需要注意一些细节:
如何判断一个节点是否是门?我们可以额外用一个 bool 型的一维数组来记录每个节点是否是门。
如何判断一个节点是否到达过?我们可以记录一个二维 bool 数组来表示。
如何求出最短路径长度?我们可以在 BFS 的过程中记录每个节点到源节点和目标节点的距离,如果相遇,则加和即为最短路径长度。
下面是本题的 Python 3 代码实现:
from collections import deque
def is_valid(walls, visited, x, y):
"""判断该位置是否可以到达"""
if x < 0 or y < 0 or x >= len(walls) or y >= len(walls[0]) or walls[x][y] or visited[x][y]:
return False
return True
def bfs(walls, visited, q, distance, start_dist, end_dist):
"""BFS 求最短路径长度"""
while q:
x, y = q.pop(0)
if start_dist[x][y] != -1 and end_dist[x][y] != -1:
return start_dist[x][y] + end_dist[x][y]
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
next_pos = (x + dx, y + dy)
if is_valid(walls, visited, *next_pos):
visited[x+dx][y+dy] = True
distance[x+dx][y+dy] = distance[x][y] + 1
q.append(next_pos)
return -1
def shortest_path(walls, doors, start, end, start_dist, end_dist):
"""求起点到终点的最短路径长度"""
visited = [[False] * len(walls[0]) for _ in range(len(walls))]
distance = [[0] * len(walls[0]) for _ in range(len(walls))]
# 将所有门加入队列中
q = deque(doors)
for x, y in doors:
visited[x][y] = True
return bfs(walls, visited, q, distance, start_dist, end_dist)
if __name__ == '__main__':
# 示例输入
t = 2
walls = [[False] * 3 + [True] * 1 + [False] * 3 + [True] * 1 + [False] * 3]
walls += [[False] * 2 + [True] * 1 + [False] * 1 + [True] * 1 + [False] * 1 + [True] * 1 + [False] * 2 + [True] * 1 + [False] * 3]
walls += [[True] * 8 + [False] * 1 + [True] * 4]
doors = [(0, 2), (1, 0), (1, 2), (1, 4), (1, 6), (2, 2), (2, 9)]
start, end = doors[0], doors[-1]
start_dist = [-1, -1, 0, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1]
end_dist = [-1, -1, 3, -1, -1, -1, 2, -1, -1, 1, -1, -1, -1]
assert shortest_path(walls, doors, start, end, start_dist, end_dist) == 3
walls = [[False] * 12 + [True] * 1]
walls += [[True] * 12 + [False] * 1]
walls += [[False] * 12 + [True] * 1]
start = (2, 0)
end = (2, 11)
doors = [(0, 0), (0, 11)]
start_dist = [8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3]
end_dist = [3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
assert shortest_path(walls, doors, start, end, start_dist, end_dist) == 6
其中,is_valid
函数是一个辅助函数,用于判断该位置是否可以到达;bfs
函数是一个基于 BFS 的求最短路径长度的算法;shortest_path
函数则是本题的解题函数,将墙和门和起点、终点的信息输入进来即可得到最短路径长度。
完整代码及注释可从 GitHub 仓库 中获取。