一、SPFA 算法(Bellman_ford 队列优化算法)
题目连接:94. 城市间货物运输 I (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——SPFA 算法
与 Bellman_ford 算法区别:只对上一次松弛时更新过的节点,作为出发节点,对其所连接的边进行松弛即可。
from collections import defaultdict, deque
import sys
class Edge:
def __init__(self, to, val):
self.to = to
self.val = val
def spfa(n ,edges, start, end):
# 创建邻接表
grid = defaultdict(list)
for p1, p2, val in edges:
grid[p1].append(Edge(p2, val))
# 初始化最短距离数组
minDist = [float('inf')] * (n + 1)
minDist[start] = 0
# 检查节点是否在队列中
isInDeque = [False] * (n + 1)
# 创建队列
que = deque()
que.append(start)
isInDeque[start] = True
while que:
node = que.popleft() # 移除队头元素
isInDeque[node] = False # 取出节点时,将其标记未在队列中
# 遍历当前节点所有边
for edge in grid[node]:
to_node = edge.to # 目标节点
val_node = edge.val # 边的权重
# 松弛操作更新最短距离
if minDist[to_node] > minDist[node] + val_node:
minDist[to_node] = minDist[node] + val_node
# 如果没有访问过,加入队列
if not isInDeque[to_node]:
que.append(to_node)
isInDeque[to_node] = True
# 检查终点的最短路径距离
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 = spfa(n, edges, start, end)
print(result)
二、Bellman_ford之判断负权回路
题目连接:95. 城市间货物运输 II (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——Bellman_ford之判断负权回路
题意:判断是否存在 负权回路。
思路:由于具有负权回路,所以每多松弛一次,最小路径就会发生变化,因此可以考虑在Bellman_ford 算法 松弛 n - 1 次的基础上,再多松弛一次,看是否会有变化,如果有变化,则说明存在负权回路。
"""
代码和 Bellman_ford 算法精讲的基本类似,只在变化部分写出注释
"""
def bellman(n, m, edges, start, end):
minDist = [float('inf')] * (n + 1)
minDist[start] = 0
# 标记是否存在负权回路
flag = False
for i in range(1, n + 1):
for from_node, to_node, val in edges:
# 松弛前 n - 1 次
if i < n:
if minDist[from_node] != float('inf') and minDist[to_node] > minDist[from_node] + val:
minDist[to_node] = minDist[from_node] + val
# 松弛第 n 次
else:
# 如果值还在减小,说明存在负权回路
if minDist[from_node] != float('inf') and minDist[to_node] > minDist[from_node] + val:
flag = True
# 如过 flag 为 True,则存在负权回路
if flag:
return "circle"
elif minDist[end] == float('inf'):
return "unconnected"
else:
return minDist[end]
if __name__ == "__main__":
n, m = map(int, input().split())
edges = []
for _ in range(m):
s, t, v = map(int, input().split())
edges.append((s, t, v))
start = 1
end = n
result = bellman(n, m, edges, start, end)
print(result)
三、Bellman_ford之单源有限最短路
题目连接:96. 城市间货物运输 III (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——Bellman_ford之单源有限最短路
思路:最多经过 k 个城市的条件下,求最短路径。注意,是最多经过 k 个城市,也可能经过比 k 少的城市。此时,我们最多经过 k + 1 条边,即松弛 k + 1 次。
Note:在每次计算 minDist 时候,要基于 对所有边上一次松弛的 minDist 数值才行,所以我们要记录上一次松弛的minDist。
def bellman(n, m, edges, start, end, k):
minDist = [float('inf')] * (n + 1)
minDist[start] = 0
# 创建一个副本数组,用来保存上一次松弛的结果
minDist_copy = [0] * (n + 1)
# 从 1 到 k + 1 遍历
for i in range(1, k + 2):
# 复制当前 minDist
minDist_copy = minDist.copy()
for from_node, to_node, val in edges:
# 判断是否可以更新,使用 copy 的数组进行判断
if minDist_copy[from_node] != float('inf') and minDist[to_node] > minDist_copy[from_node] + val:
minDist[to_node] = minDist_copy[from_node] + val
if minDist[end] == float('inf'):
return "unreachable"
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, end, k = map(int, input().split())
result = bellman(n, m, edges, start, end, k)
print(result)