一、四个核心时间(必须背)
| 符号 | 全称 | 含义 | 对象 |
|---|---|---|---|
| etv | 事件最早发生时间 | 事件最早什么时候能发生 | 顶点 |
| ltv | 事件最晚发生时间 | 事件最晚什么时候必须发生 | 顶点 |
| ete | 活动最早开始时间 | 活动最早什么时候能开始 | 边 |
| lte | 活动最晚开始时间 | 活动最晚什么时候必须开始 | 边 |
二、计算公式
1. etv(正向,取最大)
etv[v] = max(etv[u] + w)
一个事件必须等所有前置活动都完成才能发生 → 取最大值
2. ltv(反向,取最小)
ltv[u] = min(ltv[v] - w)
一个事件不能拖累后续事件 → 取最小值
3. ete(活动最早开始)
ete = etv[活动起点]
4. lte(活动最晚开始)
lte = ltv[活动终点] - 活动耗时
三、判断关键活动
ete == lte → 该活动是关键活动
所有关键活动组成的路径就是 关键路径,决定了整个项目的最短工期。
四、计算步骤(四步法)
1. 拓扑排序,得到拓扑序列
2. 正向遍历拓扑序,计算每个顶点的 etv
3. 反向遍历拓扑序,计算每个顶点的 ltv
4. 遍历每条边,计算 ete 和 lte,输出 ete == lte 的边
五、图解记忆
text
活动(边)
u -----w----> v
etv[u] 已知 etv[v] = max(etv[u] + w)
ltv[u] = min(ltv[v] - w) ltv[v] 已知
ete = etv[u]
lte = ltv[v] - w
六、代码骨架(Java)
java
// 1. 正向:计算 etv
for (int u : topo) {
for (Edge e : graph[u]) {
etv[e.to] = Math.max(etv[e.to], etv[u] + e.w);
}
}
// 2. 反向:计算 ltv
Arrays.fill(ltv, etv[n-1]);
for (int i = topo.size() - 1; i >= 0; i--) {
int u = topo.get(i);
for (Edge e : graph[u]) {
ltv[u] = Math.min(ltv[u], ltv[e.to] - e.w);
}
}
// 3. 输出关键活动
for (int u = 0; u < n; u++) {
for (Edge e : graph[u]) {
int ete = etv[u];
int lte = ltv[e.to] - e.w;
if (ete == lte) {
System.out.println(u + " -> " + e.to);
}
}
}
七、一句话总结
etv 正向取最大,ltv 反向取最小
ete = etv[起点],lte = ltv[终点] - w
ete == lte 的活动,就是关键路径
八、课
