【模板】单源最短路径

【模板】单源最短路径

时间: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++)。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值