1.Bellman_ford 队列优化算法(又名SPFA)
题目链接:Bellman_ford 队列优化算法(又名SPFA)
文档讲解: 代码随想录
Bellman_ford算法每次都对所有边进行松弛,但其实真正有效的松弛,是基于已经计算过的节点在做的松弛。使用队列来优化,只对上一次松弛更新过的节点作为出发点作为出发节点所连接的边进行松弛就可以了。在加入队列的过程可以有一个优化,用visited数组记录已经在队列里的元素,已经在队列的元素不用重复加入。
from collections import defaultdict,deque
#定义节点指向和权值
class Edge:
def __init__(self,to,val):
self.to = to
self.val = val
n,m = map(int,input().split())
edges = defaultdict(list)
for _ in range(m):
p1,p2,val = map(int,input().split())
edges[p1].append(Edge(p2,val))
#记录的是否在队列里
isque = [False] * (n+1)
start = 1
end = n
minDist = [float('inf')] * (n+1)
minDist[start] = 0
que = deque()
que.append(start)
while que:
cur = que.popleft()
#从队列里取出要取消标记
isque[cur] = False
for edge in edges[cur]:
to = edge.to
val = edge.val
#松弛
if minDist[cur] + val < minDist[to]:
minDist[to] = minDist[cur] + val
if isque[to] == False:
que.append(to)
isque[to] = True
if minDist[end] == float('inf'):
print("unconnected")
else:
print(minDist[end])
图越稀疏,SPFA的效率就越高。一般来说,SPFA的时间复杂度为 O ( K ∗ N ) O(K*N) O(K∗N),K为不定值,因为节点需要计入几次队列取决于图的稠密度。如果存在正权环路,即使元素重复加入队列,最后也会因为所有边都松弛后,节点数量(minDist数组)不再发生变化而终止。
2.bellman_ford之判断负权回路
题目链接:bellman_ford之判断负权回路
文档讲解: 代码随想录
这道题和上一题的区别在于存在负权环路,在bellman_ford算法中,对于minDist数组,松弛n-1次所有边就可以得到起点到其他节点的最短距离,松弛n次以上,数组中的结果也不会改变。而如果存在负权环路,一直会有更短的最短路,所以松弛第n次,minDIst数组也会发生改变。那么本题判断是否存在负权环路的核心在于,松弛第n次,看minDist数组是否发生变化。
n,m = map(int,input().split())
grid = []
for _ in range(m):
grid.append(list(map(int,input().split())))
start = 1
end = n
minDist = [float('inf')] * (n+1)
minDist[start] = 0
flag = False
for i in range(n+1):
for p1,p2,val in grid:
if i < n:
if minDist[p1] != float('inf') and minDist[p1] + val < minDist[p2]:
minDist[p2] = minDist[p1] + val
else:
if minDist[p1] != float('inf') and minDist[p1] + val < minDist[p2]:
flag = True
if flag:
print("circle")
elif minDist[end] == float('inf'):
print("unconnected")
else:
print(minDist[end])
3.bellman_ford之单源有限最短路
题目链接:bellman_ford之单源有限最短路
文档讲解: 代码随想录
本题为单源有限最短路问题, 最多经过k个城市,那么是k+1条边相连的节点。对所有边松弛k+1次,就是求起到到达与起点k+1条边相连的节点的最短距离。在对所有边进行松弛的过程中,有可能因为边的顺序导致所有节点都更新,这就可能违背至多经过k个节点的限制。所以在每次计算minDist数组时,要基于对所有边上一次松弛的minDist数值才行。
n,m = map(int,input().split())
grid = []
for _ in range(m):
grid.append(list(map(int,input().split())))
src,dst,k = map(int,input().split())
minDist = [float('inf')] * (n+1)
minDist[src] = 0
for _ in range(k+1):
minDist_copy = minDist[:]
for s,t,v in grid:
if minDist_copy != float('inf') and minDist_copy[s] + v < minDist[t]:
minDist[t] = minDist_copy[s] + v
if minDist[dst] == float('inf'):
print("unreachable")
else:
print(minDist[dst])