有向无环图(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:算法步骤
- 拓扑排序:使用 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 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 的无环性质,确保在更新每个顶点时,其所有入边对应的前驱顶点都已被正确处理。