【模板】单源最短路径
时间:2023.11.10
题目地址:单源最短路径(标准版)
单源最短路径(弱化版)
算法介绍:最短路
题目分析
求最短路径的几个方法:Bellman–Ford 算法、SPFA优化、Dijkstra 算法(优先队列)
前两个可以做带负权的,最后一个只能是不带负权边的图。
代码
① Bellman–Ford 算法
这个算法和spfa一样超时,或者有数据过不了。
n, m, s = map(int, input().split())
e = [[] for _ in range(n+1)]
for _ in range(m):
u, v, w = map(int, input().split())
e[u].append((v, w))
ans = [float('inf')]*(n+1) # 记录源点到各点最短距离
def bellmanford(n, s, ans):
ans[s] = 0
for i in range(1, n+1):
flag = False # 判断一轮循环过程中是否发生松弛操作(就是更新距离变小)
for u in range(1, n+1):
if ans[u] == float('inf'):
continue
# 无穷大与常数加减仍然为无穷大
# 因此最短路长度为 inf 的点引出的边不可能发生松弛操作
for ed in e[u]:
v, w = ed[0], ed[1]
if ans[v] > ans [u] + w:
ans[v] = ans[u] + w
flag = True
# 没有可以松弛的边时就停止算法
if not flag:
break
# 第 n 轮循环仍然可以松弛时说明 s 点可以抵达一个负环
return flag
bellmanford(n, s, ans)
for i in ans[1:]:
print(i, end=' ')
② SPFA优化
对于上述题目用这个方法,数据卡了SPFA,所以ac不了,spfa不稳定。对于无负权的还是推荐用Dijkstra 算法。
from collections import deque
n, m, s = map(int, input().split())
e = [[] for _ in range(n+1)]
# rela = [[0]*(n+1) for _ in range(n+1)]
for _ in range(m):
u, v, w = map(int, input().split())
# if rela[u][v]:
# if rela[u][v] > w:
# e[u].remove((v, rela[u][v]))
# e[u].append((v, w))
# rela[u][v] = w
# continue
e[u].append((v, w))
# rela[u][v] = w
ans = [float('inf')]*(n+1) # 记录源点到各点最短距离
cnt = [0]*(n+1) # 记录源点到各点经历的边数
vis = [False]*(n+1) # 判断是否在队列中
q = deque()
def spfa(n, s, q, cnt, vis, ans):
ans[s] = 0
q.append(s)
vis[s] = True
while q:
cur = q.popleft()
vis[cur] = False
for ed in e[cur]:
v, w = ed[0], ed[1]
if ans[v] > ans[cur] + w: # 更新最短距离
ans[v] = ans[cur] + w
cnt[v] = cnt[v] + 1
if cnt[v] >= n:
return False
# 在不经过负环的情况下,最短路至多经过 n - 1 条边
# 因此如果经过了多于 n 条边,一定说明经过了负环
if not vis[v]:
q.append(v)
vis[v] = True
return True
spfa(n, s, q, cnt, vis, ans)
for i in ans[1:]:
print(i, end=' ')
③ Dijkstra 算法
处理上述的不带负权的图,Dijkstra 算法。
from queue import PriorityQueue
n, m, s = map(int, input().split())
e = [[] for _ in range(n+1)]
for _ in range(m):
u, v, w = map(int, input().split())
e[u].append((v, w))
ans = [float('inf')]*(n+1) # 记录源点到各点最短距离
vis = set()
def dijkstra(s, vis, ans):
pq = PriorityQueue()
pq.put((0, s))
ans[s] = 0
while not pq.empty():
_, u = pq.get()
if u in vis:
continue
vis.add(u)
for v, w in e[u]:
if ans[v] > ans[u] + w:
ans[v] = ans[u] + w
pq.put((ans[v], v))
return ans
dijkstra(s, vis, ans)
for i in ans[1:]:
print(i, end=' ')
小结
上述给的代码均不能AC,这里只是为了记录一下这个模板,还有一些没考虑到的问题,会导致超时,爆内存(当中对于python可能就不是很友好,因为python本来就慢于c++)。