有向无环图(DAG)的最长路径(Longest Path)问题
字数 1830 2025-12-14 05:21:26

有向无环图(DAG)的最长路径(Longest Path)问题


题目描述

给定一个 有向无环图(DAG),以及每条边的 权重(可以是正数、负数或零),要求找出从某个 源点 到其他所有顶点的 最长路径长度(即路径上所有边的权重之和最大)。如果从源点无法到达某个顶点,则其最长路径长度为 负无穷(或标记为不可达)。

这个问题可以看作 DAG 的单源最短路径问题 的“变体”——只需将所有权重取相反数,并运行最短路径算法即可。但由于 DAG 具有 无环 的性质,我们可以更高效地通过 拓扑排序 + 动态规划 直接求解最长路径。


解题过程

步骤 1:理解 DAG 的性质

DAG 中 没有有向环,因此我们可以对顶点进行 拓扑排序,得到一个顶点序列,使得所有边的方向都是从序列中靠前的顶点指向靠后的顶点。
这种排序保证了:当我们按照拓扑顺序处理顶点时,对于当前顶点 v,所有可能到达 v 的顶点都已经被处理过。


步骤 2:问题转化为动态规划

设:

  • dist[v] 表示从 源点 s 到顶点 v 的最长路径长度。
  • 初始时,dist[s] = 0,其他顶点 dist[u] = -∞(表示尚未到达)。
  • 对于图中的每条边 (u, v),权重为 w(u, v),更新规则为:

\[ dist[v] = \max(dist[v],\; dist[u] + w(u, v)) \]

因为 DAG 中无环,我们按照拓扑顺序依次处理顶点 u,并 松弛 所有从 u 出发的边。这样可以确保在更新 dist[v] 时,dist[u] 已经是最终的最优值。


步骤 3:算法步骤

  1. 拓扑排序:使用 Kahn 算法或 DFS 得到 DAG 的拓扑序列。
  2. 初始化距离数组
    • dist[s] = 0
    • 对于其他顶点 v ≠ sdist[v] = -∞
  3. 按拓扑顺序遍历每个顶点 u
    • 如果 dist[u] 仍为 -∞,说明从 s 无法到达 u,跳过。
    • 否则,对于 u 的每个邻居 v(通过边 (u, v)):
      • 更新 dist[v] = max(dist[v], dist[u] + w(u, v))
  4. 输出结果dist[v] 即为从 sv 的最长路径长度。

步骤 4:举例说明

考虑以下 DAG(顶点编号从 0 开始,源点 s = 0):
边集(格式:u v w):

0 1 5
0 2 3
1 2 2
1 3 6
2 3 7
2 4 4
3 4 1

拓扑排序(可能的顺序):0, 1, 2, 3, 4

初始化:

dist = [0, -∞, -∞, -∞, -∞]

按顺序处理:

  • 处理顶点 0:
    更新邻居:
    dist[1] = max(-∞, 0+5) = 5
    dist[2] = max(-∞, 0+3) = 3
  • 处理顶点 1:
    更新邻居:
    dist[2] = max(3, 5+2) = 7
    dist[3] = max(-∞, 5+6) = 11
  • 处理顶点 2:
    更新邻居:
    dist[3] = max(11, 7+7) = 14
    dist[4] = max(-∞, 7+4) = 11
  • 处理顶点 3:
    更新邻居:
    dist[4] = max(11, 14+1) = 15
  • 处理顶点 4:无出边。

最终结果:

顶点 0: 0
顶点 1: 5
顶点 2: 7
顶点 3: 14
顶点 4: 15

步骤 5:算法复杂度

  • 拓扑排序:O(V + E)
  • 动态规划更新:每个顶点处理其所有出边,共 O(E)
    总复杂度:O(V + E),非常高效。

步骤 6:扩展与应用

  • 最长路径的具体路径:在更新 dist[v] 时,同时记录 prev[v] = u,最后反向回溯即可得到路径。
  • DAG 的最长路径问题 可用于:
    • 项目管理中的 关键路径法(CPM)
    • 编译器中的指令调度
    • 依赖关系图中的最长链计算

步骤 7:注意事项

  • 如果图中 存在正环,则最长路径可能无界增长,但 DAG 没有环,因此问题总是有解。
  • 如果所有权重都是负数,最长路径实际上是“最短路径”的相反数问题。
  • 对于 无向无环图(即树),最长路径可通过两次 DFS/BFS 求直径,但 DAG 的最长路径是 有向 的,必须按拓扑顺序求解。

总结

通过 拓扑排序 + 动态规划,我们可以在 O(V+E) 时间内高效求解 DAG 的单源最长路径问题。这个算法利用了 DAG 的无环性质,确保在更新每个顶点时,其所有入边对应的前驱顶点都已被正确处理。

有向无环图(DAG)的最长路径(Longest Path)问题 题目描述 给定一个 有向无环图(DAG) ,以及每条边的 权重 (可以是正数、负数或零),要求找出从某个 源点 到其他所有顶点的 最长路径长度 (即路径上所有边的权重之和最大)。如果从源点无法到达某个顶点,则其最长路径长度为 负无穷 (或标记为不可达)。 这个问题可以看作 DAG 的单源最短路径问题 的“变体”——只需将所有权重取相反数,并运行最短路径算法即可。但由于 DAG 具有 无环 的性质,我们可以更高效地通过 拓扑排序 + 动态规划 直接求解最长路径。 解题过程 步骤 1:理解 DAG 的性质 DAG 中 没有有向环 ,因此我们可以对顶点进行 拓扑排序 ,得到一个顶点序列,使得所有边的方向都是从序列中靠前的顶点指向靠后的顶点。 这种排序保证了:当我们按照拓扑顺序处理顶点时,对于当前顶点 v ,所有可能到达 v 的顶点都已经被处理过。 步骤 2:问题转化为动态规划 设: dist[v] 表示从 源点 s 到顶点 v 的最长路径长度。 初始时, dist[s] = 0 ,其他顶点 dist[u] = -∞ (表示尚未到达)。 对于图中的每条边 (u, v) ,权重为 w(u, v) ,更新规则为: \[ dist[ v] = \max(dist[ v],\; dist[ u ] + w(u, v)) \] 因为 DAG 中无环,我们按照拓扑顺序依次处理顶点 u ,并 松弛 所有从 u 出发的边。这样可以确保在更新 dist[v] 时, dist[u] 已经是最终的最优值。 步骤 3:算法步骤 拓扑排序 :使用 Kahn 算法或 DFS 得到 DAG 的拓扑序列。 初始化距离数组 : dist[s] = 0 对于其他顶点 v ≠ s , dist[v] = -∞ 按拓扑顺序遍历每个顶点 u : 如果 dist[u] 仍为 -∞ ,说明从 s 无法到达 u ,跳过。 否则,对于 u 的每个邻居 v (通过边 (u, v) ): 更新 dist[v] = max(dist[v], dist[u] + w(u, v)) 输出结果 : dist[v] 即为从 s 到 v 的最长路径长度。 步骤 4:举例说明 考虑以下 DAG(顶点编号从 0 开始,源点 s = 0 ): 边集(格式: u v w ): 拓扑排序 (可能的顺序): 0, 1, 2, 3, 4 初始化: 按顺序处理: 处理顶点 0: 更新邻居: dist[1] = max(-∞, 0+5) = 5 dist[2] = max(-∞, 0+3) = 3 处理顶点 1: 更新邻居: dist[2] = max(3, 5+2) = 7 dist[3] = max(-∞, 5+6) = 11 处理顶点 2: 更新邻居: dist[3] = max(11, 7+7) = 14 dist[4] = max(-∞, 7+4) = 11 处理顶点 3: 更新邻居: dist[4] = max(11, 14+1) = 15 处理顶点 4:无出边。 最终结果: 步骤 5:算法复杂度 拓扑排序: O(V + E) 动态规划更新:每个顶点处理其所有出边,共 O(E) 总复杂度: O(V + E) ,非常高效。 步骤 6:扩展与应用 最长路径的具体路径 :在更新 dist[v] 时,同时记录 prev[v] = u ,最后反向回溯即可得到路径。 DAG 的最长路径问题 可用于: 项目管理中的 关键路径法(CPM) 编译器中的指令调度 依赖关系图中的最长链计算 步骤 7:注意事项 如果图中 存在正环 ,则最长路径可能无界增长,但 DAG 没有环 ,因此问题总是有解。 如果所有权重都是负数,最长路径实际上是“最短路径”的相反数问题。 对于 无向无环图(即树) ,最长路径可通过两次 DFS/BFS 求直径,但 DAG 的最长路径是 有向 的,必须按拓扑顺序求解。 总结 通过 拓扑排序 + 动态规划 ,我们可以在 O(V+E) 时间内高效求解 DAG 的单源最长路径问题。这个算法利用了 DAG 的无环性质,确保在更新每个顶点时,其所有入边对应的前驱顶点都已被正确处理。