一、dijkstra(堆优化版)
题目连接:47. 参加科学大会(第六期模拟笔试) (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——dijkstra(堆优化版)
思路依旧是dijkstra算法三部曲:
step1. 选择距离源节点近且未被访问过的节点;
区别:直接遍历边,且通过堆对边进行排序,达到直接选择距离源节点最近的节点。
step2. 将该节点标记为访问过;
step3. 更新非访问节点到远点的距离,即更新 minDist 数组。
import heapq # 用于实现优先队列
from collections import defaultdict
# 定义一个表示带权重的边的类
class Edge:
def __init__(self, to, val):
self.to = to # 邻接顶点
self.val = val # 边的权重
def dijkstra(n, m, edges, start, end):
# 创建邻接表,使用 defaultdict 存储边
grid = defaultdict(list)
# 填充邻接表,存储每条边的信息
for p1, p2, val in edges:
grid[p1].append(Edge(p2, val))
# 存储从源点到每个节点的最短距离,初始化为无穷大
minDist = [float('inf')] * (n + 1)
minDist[start] = 0
# 记录节点是否被访问过
visited = [False] * (n + 1)
# 优先队列,存储(节点,源点到该节点的距离)的元组
pq = []
# pq 是一个列表,作为优先队列使用
# (0, start) 表示从 start 起始节点到源节点的距离为0
heapq.heappush(pq, (0, start))
while pq:
# step1. 选择距离源节点近且未被访问过的节点
# cur_node -> 当前节点
# cur_dist -> 到当前节点的距离
cur_dist, cur_node = heapq.heappop(pq)
# 如果当前节点访问过,跳过
if visited[cur_node]:
continue
# step2. 将该节点标记为访问过
visited[cur_node] = True
# step3. 更新非访问节点到远点的距离
# 遍历当前节点所有的边
for edge in grid[cur_node]:
# 如果相邻节点没访问过
if not visited[edge.to]:
# 且 通过当前节点 cur_node 到达相邻节点 edge.to 的路径(当前到达源节点距离+道道相邻节点距离)是否比已知到达相邻节点距离(minDist[edge.to])最短路径更短
if cur_dist + edge.val < minDist[edge.to]:
# 更新最短距离
minDist[edge.to] = cur_dist + edge.val
# 把(节点,源点到该节点的距离)
heapq.heappush(pq, (minDist[edge.to], edge.to))
# 检查目标路径是否可以到到
if minDist[end] == float('inf'):
return -1
else:
return minDist[end]
if __name__ == '__main__':
n, m = map(int, input().split())
edges = []
# 读取每条边信息
for _ in range(m):
p1, p2, val = map(int, input().split())
edges.append((p1, p2, val))
start = 1
end = n
result = dijkstra(n, m, edges, start, end)
print(result)
二、Bellman_ford 算法精讲
题目连接:94. 城市间货物运输 I (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——Bellman_ford 算法
应用场景:解决带负权值的单源最短路径问题。
核心思想:对 所有边 进行松弛 n - 1 次操作(n 为节点数量),从而求得目标最短路。
松弛定义:如果 通过 A 到 B 这条边可以获得更短的到达B节点的路径,即如果 minDist[B] > minDist[A] + value
,那么我们就更新 minDist[B] = minDist[A] + value
,这个过程就叫做 “松弛” 。代码:minDist[B] = min(minDist[A] + value, minDist[B])
Note:松弛 n - 1 次因为对所有边每松弛一次,相当于计算 起点 到达与起点用一条边相连的节点的最短距离,无论图什么样,松弛 n - 1 次一定可以得到 起点 到 所有点 的最短距离。
def bellman(n, m, edges, start, end):
# 初始化最短距离数组
minDist = [float('inf')] * (n + 1)
minDist[start] = 0
# 对所有边松弛 n - 1 次
for _ in range (1, n):
# 遍历所有边
for from_node, to_node, val in edges:
# 松弛操作
if minDist[from_node] != float('inf'):
minDist[to_node] = min(minDist[from_node] + val, minDist[to_node])
# 检查是否可以到达终点
if minDist[end] == float('inf'):
return "unconnected"
else:
return minDist[end]
if __name__ == '__main__':
n, m = map(int, input().split())
edges = []
for _ in range(m):
p1, p2, val = map(int, input().split())
edges.append((p1, p2, val))
start = 1
end = n
result = bellman(n, m, edges, start, end)
print(result)