最小环问题(有向图与无向图的 Floyd 算法求解)
问题描述
给定一个带权有向图或带权无向图(图中允许存在负权边,但不能存在负权环),要求找出图中长度最小的环。这里环的长度定义为环上所有边的权值之和。这是一个经典的图论问题,在实际应用中可用于检查网络、交通或社交关系中的最小环路。
解题思路
对于最小环问题,我们可以利用 Floyd-Warshall 算法进行求解。Floyd 算法原本用于求解所有顶点对之间的最短路径,但我们可以在其动态规划过程中,同步计算经过某个顶点 k 的环的长度。核心思路是:
设 dist[i][j] 表示当前(使用前 k-1 个顶点作为中间点)从 i 到 j 的最短路径长度,graph[i][j] 表示边的原始权值(若 i 到 j 无边则为 ∞)。
在 Floyd 算法的主循环中,当我们将顶点 k 作为新的中间点尝试更新最短路径前,对所有 i < k 且 j < k(且 i ≠ j),我们可以检查 dist[i][j] + graph[j][k] + graph[k][i] 是否构成一个经过 k 的环(注意在有向图中方向要一致)。这样我们能枚举所有以 k 为最大编号顶点的环,并更新最小环长度。
具体步骤
-
初始化
- 输入图的顶点数 n 和边信息。
- 创建二维数组
graph[n][n],初始时graph[i][j]表示边 i→j 的权值,若无边则为 ∞(用一个大数 INF 表示),graph[i][i] = 0。 - 复制
graph到dist,作为 Floyd 算法中的最短路径矩阵。 - 初始化
min_cycle = INF。
-
Floyd 算法主循环
枚举中间点 k 从 0 到 n-1:
a. 检查环:
对所有 i < k 且 j < k(且 i ≠ j),如果dist[i][j] != INF且graph[j][k] != INF且graph[k][i] != INF,则计算:
cycle_length = dist[i][j] + graph[j][k] + graph[k][i]
这表示一个环 i → ... → j → k → i(其中 i→j 的路径不经过 k,因为此时 dist[i][j] 是使用前 k-1 个顶点作为中间点计算出的最短路径)。
更新min_cycle = min(min_cycle, cycle_length)。
b. 更新最短路径:
对所有 i, j,如果dist[i][k] != INF且dist[k][j] != INF,则尝试更新:
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])。
(注意:这一步必须在检查环之后进行,否则 dist[i][j] 可能已经包含 k,导致环的检查错误) -
输出结果
- 如果
min_cycle == INF,说明图中不存在环;否则输出min_cycle。
- 如果
算法正确性说明
- 每个环都可以由环中编号最大的顶点 k 来代表(有多个最大编号时任意取一个)。
- 当枚举到 k 作为中间点时,
dist[i][j]存储的是只使用顶点 0..k-1 作为中间点的最短 i→j 路径,因此 i→j 的路径不包含 k。 - 此时环 i → ... → j → k → i 的长度正好是
dist[i][j] + graph[j][k] + graph[k][i],且该环被恰好检查一次(当 k 是其最大编号顶点时)。 - 对于无向图,
graph[j][k]和graph[k][i]是双向对称的,算法同样适用(注意无向图中环至少需要 3 个不同顶点,避免将一条边往返当成环)。
复杂度分析
- 时间复杂度:O(n³),与 Floyd 算法相同。
- 空间复杂度:O(n²),用于存储邻接矩阵和最短路径矩阵。
举例说明(无向图)
考虑一个 4 个顶点的无向图,边权如下:
0-1: 1, 0-2: 3, 1-2: 2, 2-3: 4, 3-0: 5。
初始 graph 矩阵(对称):
[[0, 1, 3, 5],
[1, 0, 2, INF],
[3, 2, 0, 4],
[5, INF, 4, 0]]
运行上述算法:
- k=0 时,i,j 均小于 0,不检查环。更新最短路径。
- k=1 时,检查 i=0,j=0? 跳过(i≠j)。实际 i=0,j 无小于 1 且 i≠j 的,不检查。更新最短路径(例如 dist[0][2] 更新为 1+2=3)。
- k=2 时,检查 i=0,j=1:dist[0][1]=1,graph[1][2]=2,graph[2][0]=3,环长=1+2+3=6 → 更新 min_cycle=6。
更新最短路径。 - k=3 时,检查 i=0,j=1:dist[0][1]=1,graph[1][3]=INF → 跳过;
i=0,j=2:dist[0][2]=3,graph[2][3]=4,graph[3][0]=5,环长=3+4+5=12;
i=1,j=2:dist[1][2]=2,graph[2][3]=4,graph[3][1]=INF → 跳过。
最终最小环长度为 6(环 0-1-2-0)。
注意事项
- 图中不能有负权环,否则最小环无意义(趋向负无穷)。
- 若图是带权有向图,计算环时方向必须一致:
dist[i][j]是 i→j,加上 j→k 和 k→i 的边。 - 如需输出环的具体路径,可在更新
min_cycle时记录对应的 i,j,k,然后通过反向追踪 dist 矩阵重构路径。