Johnson全源最短路径 - Python模板

算法 | 最短路 | 图论 | 模板 | python

2025年3月9日

此文章已过时 {% post_link Python-最短路 %}

简介

模板使用defaultdict实现, 不必要时可以更换list减少内存开销. 有结果时返回一个字典, 有负环返回None. 查询方式print(johnson(e, nodes)[start][end])

代码

from random import randint
from collections import defaultdict, deque
import heapq


def dijkstra(e, s):
    vis = set()
    queue = [(0, s)]
    dis = defaultdict(lambda: float("+inf"))
    dis[s] = 0
    while queue:
        _, u = heapq.heappop(queue)
        if u in vis:
            continue
        vis.add(u)
        for v, w in e[u]:
            if dis[v] > dis[u] + w:
                dis[v] = dis[u] + w
                heapq.heappush(queue, (dis[v], v))
    return dis


def spfa(e, s):
    dis = defaultdict(lambda: float("+inf"))
    dis[s] = 0
    queue = deque([s])
    in_queue = defaultdict(bool)
    in_queue[s] = True
    count = defaultdict(int)
    count[s] = 1

    while queue:
        u = queue.popleft()
        in_queue[u] = False

        for v, w in e[u]:
            if dis[v] > dis[u] + w:
                dis[v] = dis[u] + w
                if not in_queue[v]:
                    queue.append(v)
                    in_queue[v] = True
                    count[v] += 1
                    if count[v] > len(e):
                        return None
    return dis


def johnson(e, nodes):
    virtual_node = hex(randint(1 << (4 * 4), 1 << (5 * 4)))
    e[virtual_node] = [(node, 0) for node in nodes]

    h = spfa(e, virtual_node)
    if h is None:
        return None

    new_edges = defaultdict(list)
    for u in e:
        for v, w in e[u]:
            new_edges[u].append((v, w + h[u] - h[v]))

    result = defaultdict(lambda: defaultdict(lambda: float("+inf")))
    for u in nodes:
        dist = dijkstra(new_edges, u)
        result[u].update({v: dist[v] - h[u] + h[v] for v in dist if v != virtual_node})

    return result

使用案例

# P7551 [COCI 2020/2021 #6] Alias

e = defaultdict(list)
points = set()
n, m = map(int, input().split())
for i in range(m):
    a, b, t = input().split()
    e[a].append((b, int(t)))
    points.add(a)
    points.add(b)


dis = johnson(e, points)

for i in range(int(input())):
    a, b = input().split()
    if dis[a][b] < float("+inf"):
        print(dis[a][b])
    else:
        print("Roger")