最小环问题(有向图与无向图的 Floyd 算法求解)
字数 2399 2025-12-14 18:55:40

最小环问题(有向图与无向图的 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 为最大编号顶点的环,并更新最小环长度。


具体步骤

  1. 初始化

    • 输入图的顶点数 n 和边信息。
    • 创建二维数组 graph[n][n],初始时 graph[i][j] 表示边 i→j 的权值,若无边则为 ∞(用一个大数 INF 表示),graph[i][i] = 0
    • 复制 graphdist,作为 Floyd 算法中的最短路径矩阵。
    • 初始化 min_cycle = INF
  2. Floyd 算法主循环
    枚举中间点 k 从 0 到 n-1:
    a. 检查环
    对所有 i < k 且 j < k(且 i ≠ j),如果 dist[i][j] != INFgraph[j][k] != INFgraph[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] != INFdist[k][j] != INF,则尝试更新:
    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
    (注意:这一步必须在检查环之后进行,否则 dist[i][j] 可能已经包含 k,导致环的检查错误)

  3. 输出结果

    • 如果 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 矩阵重构路径。
最小环问题(有向图与无向图的 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 矩阵重构路径。