📅  最后修改于: 2023-12-03 15:42:22.380000             🧑  作者: Mango
该题目为《门|门CS 2008》第28题,是一道经典的图论题目。该题目考察了最短路算法,需要使用Dijkstra算法或者SPFA算法求解。
给定一个N个点,M条边的有向图,每条边有一个权值,求从源点到汇点的最短路径长度。如果不存在从源点到汇点的路径,则输出-1。
第一行包含三个整数N,M,S,其中S表示源点。
接下来M行,每行包含三个整数a,b,c,表示存在一条从a到b的有向边,边权为c。
输出一个整数,表示从源点到汇点的最短路径长度。如果不存在从源点到汇点的路径,则输出-1。
本题可以使用Dijkstra算法或者SPFA算法求解最短路径长度。这里以Dijkstra算法为例进行介绍。
Dijkstra算法是一种贪心算法,用来求解有向图中单源最短路径的问题。该算法维护了一个集合S,表示已经求得最短路径的结点集合。算法的思想是:从源点开始不断扩展S,直到扩展到汇点或者无法再扩展为止。
算法流程如下:
初始化:设源点为s,将所有结点的最短路径长度初始化为inf,将源点的最短路径长度初始化为0。将所有点标记为未访问。
迭代:从未访问的结点中,选择距离源点最近的结点u,将其标记为已访问,并尝试更新其它结点的最短路径。
更新:对于u的所有未访问的邻居结点v,如果源点到v的最短路径大于源点到u的最短路径加上(u,v)的边权,则更新源点到v的最短路径长度。
终止条件:如果终点已经被访问,表示已经找到最短路径并返回其长度;如果未访问的结点中没有结点距离源点小于inf,则表示不存在从源点到终点的路径。
from collections import defaultdict
import heapq
def dijkstra(graph, start_node, end_node):
# 初始化最短路径长度
dist = {node: float('inf') for node in graph}
dist[start_node] = 0
# 初始化堆,将起点加入堆
heap = [(0, start_node)]
# 初始化父结点
parent = defaultdict(list)
# Dijkstra算法开始
while heap:
# 取出堆顶结点
(cost, current_node) = heapq.heappop(heap)
# 如果当前结点已经处理过,直接跳过
if cost > dist[current_node]:
continue
# 遍历当前结点的邻居结点
for neighbor, weight in graph[current_node].items():
# 更新邻居结点的最短路径长度
if dist[current_node] + weight < dist[neighbor]:
dist[neighbor] = dist[current_node] + weight
parent[neighbor] = [current_node]
heapq.heappush(heap, (dist[neighbor], neighbor))
elif dist[current_node] + weight == dist[neighbor]:
parent[neighbor].append(current_node)
# 检查是否到达终点
if neighbor == end_node:
return (dist[end_node], parent)
# 如果到达这里,表示不存在从源点到终点的路径
return (-1, None)
# 样例输入
n, m, s = 5, 7, 1
edges = [(1, 2, 2), (1, 3, 3), (2, 4, 4), (2, 5, 5), (3, 4, 1), (3, 5, 6), (4, 5, 2)]
# 构建邻接表
graph = defaultdict(dict)
for a, b, c in edges:
graph[a][b] = c
# 运行Dijkstra算法
dist, parent = dijkstra(graph, s, n)
# 输出结果
if dist == -1:
print("-1")
else:
print(dist)
代码中使用了一个辅助数组parent,用来记录每个结点的父结点。如果存在多条最短路径,需要记录所有的父结点,因此使用了一个list来存储父结点。