📅  最后修改于: 2023-12-03 15:18:44.120000             🧑  作者: Mango
本题是一道典型的求解最短路的问题,假设有一个城市的地图,地图上标记了若干个位置,每个位置的编号为 $1,2,\dots,n$,其中编号为 $1$ 的位置是警局,编号为 $n$ 的位置是小偷的藏身之处。每个位置都有一些通往其他位置的小路,每条小路的长度为 $1$(见下图)。
警察需要通过某个程序,找到一条从警局($1$号位置)出发,到达小偷的藏身之处($n$号位置)的路径,这条路径上包含的边数必须不超过 $k$ 条,求这样的路径的最短长度。
为了求解这个问题,我们可以运用广度优先搜索(BFS)算法,设 $dis_i$ 表示从 $1$ 号位置到 $i$ 号位置的最短距离,那么有以下的转移方法:
从 $1$ 号位置开始搜索,将所有与编号为 $1$ 的位置相邻的位置(即与 $1$ 号位置之间有一条边相连的位置)加入队列中,同时把这些点的 $dis$ 的值附为 $1$。
每次从队列中取出一个点 $u$,找出所有与 $u$ 相邻的没被搜索过的位置 $v$,把它的 $dis_v$ 赋为 $dis_u+1$,然后把 $v$ 加入队列中。
当队列为空时,搜索结束,此时 $dis_n$ 的值即为 $1$ 号位置到 $n$ 号位置的最短距离。
代码展示如下:
def bfs(n:int, k:int, graph:List[List[int]]) -> int:
# 初始时所有点的距离赋为 -1,表示未被访问
dis = [-1] * n
# 开始位置为 1 号点,距离为 0
q = deque([(1, 0)])
# 记录已经访问过的点(避免重复访问)
visited = [False] * n
visited[0] = True # 1 号点已经被访问过了,无需再访问
while q:
u, u_dis = q.popleft()
# 如果 u 到 n 的距离已经超过了 k,显然不符合题意,直接跳过
if u_dis > k:
break
# 如果 u 为 n 号点,且当前距离不大于 k,说明找到了一条路径,直接返回该路径长度
if u == n:
return u_dis
# 找出与 u 相邻的未被访问的点,加入队列中
for v in graph[u-1]:
if not visited[v-1]:
visited[v-1] = True
q.append((v, u_dis+1))
# 如果搜索结束时还没有找到合法路径,则无解,返回 -1
return -1
以上是求解本题的核心算法,完整的 Python 代码如下:
from typing import List
from collections import deque
def bfs(n:int, k:int, graph:List[List[int]]) -> int:
# 初始时所有点的距离赋为 -1,表示未被访问
dis = [-1] * n
# 开始位置为 1 号点,距离为 0
q = deque([(1, 0)])
# 记录已经访问过的点(避免重复访问)
visited = [False] * n
visited[0] = True # 1 号点已经被访问过了,无需再访问
while q:
u, u_dis = q.popleft()
# 如果 u 到 n 的距离已经超过了 k,显然不符合题意,直接跳过
if u_dis > k:
break
# 如果 u 为 n 号点,且当前距离不大于 k,说明找到了一条路径,直接返回该路径长度
if u == n:
return u_dis
# 找出与 u 相邻的未被访问的点,加入队列中
for v in graph[u-1]:
if not visited[v-1]:
visited[v-1] = True
q.append((v, u_dis+1))
# 如果搜索结束时还没有找到合法路径,则无解,返回 -1
return -1
def main():
n, k = map(int, input().split())
graph = [[] for _ in range(n)]
for i in range(n):
nodes = list(map(int, input().split()))[1:]
graph[i].extend(nodes)
print(bfs(n, k, graph))
if __name__ == '__main__':
main()
以上代码中,bfs
函数是最短路算法的实现,main
函数则是读入数据并输出结果的核心逻辑。在输入数据时,我们需要把图存储为邻接表的形式,以便在 BFS 中快速地找到一个节点的相邻节点。