前置知识: 算法与数据结构

Floyd-Warshall

2 minIntermediate2026/6/14

Floyd-Warshall 多源最短路径算法:动态规划推导、路径重建、负环检测与传递闭包。

1. 算法原理

1.1 动态规划定义

dp[k][i][j]=从 i 到 j,只经过节点 {0,1,,k} 的最短路径dp[k][i][j] = \text{从 } i \text{ 到 } j \text{,只经过节点 } \{0, 1, \ldots, k\} \text{ 的最短路径}

1.2 状态转移

dp[k][i][j]=min(dp[k1][i][j], dp[k1][i][k]+dp[k1][k][j])dp[k][i][j] = \min(dp[k-1][i][j],\ dp[k-1][i][k] + dp[k-1][k][j])

含义:要么不经过 kk,要么经过 kkikji \to k \to j)。

1.3 空间优化

kk 维可以省略,原地更新:

def floyd_warshall(dist):
    n = len(dist)
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    return dist

2. 路径重建

2.1 记录中间节点

def floyd_with_path(dist):
    n = len(dist)
    nxt = [[j if dist[i][j] < float('inf') else -1 for j in range(n)] for i in range(n)]

    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
                    nxt[i][j] = nxt[i][k]

    return dist, nxt

def reconstruct_path(nxt, i, j):
    if nxt[i][j] == -1:
        return []
    path = [i]
    while i != j:
        i = nxt[i][j]
        path.append(i)
    return path

3. 负环检测

3.1 检测方法

Floyd 完成后,如果 dist[i][i] < 0,则存在经过 ii 的负环:

def has_negative_cycle(dist):
    n = len(dist)
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    for i in range(n):
        if dist[i][i] < 0:
            return True
    return False

4. 传递闭包

4.1 可达性矩阵

将 Floyd 用于判断节点间是否可达:

def transitive_closure(reach):
    n = len(reach)
    for k in range(n):
        for i in range(n):
            for j in range(n):
                reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j])
    return reach

4.2 位运算优化

def transitive_closure_bitwise(reach):
    n = len(reach)
    for k in range(n):
        for i in range(n):
            if reach[i] & (1 << k):
                reach[i] |= reach[k]
    return reach

5. 复杂度与应用

维度
时间O(n3)O(n^3)
空间O(n2)O(n^2)
适用n400n \leq 400,多源最短路

适用场景:稠密、需要所有节点对最短路、负权边(无负环)。