關鍵路徑法 (Critical Path Method, CPM) 用於專案管理中計算最早/最晚開始時間、浮動時間,識別決定專案工期的關鍵活動。本文提供基於鄰接表的 Java 實現,輸出所有關鍵路徑邊的並集。
圖以二維陣列 int[][] graph 表示鄰接表:
graph[v][0]:頂點 v 的出度(邊數)。graph[v][2*i-1]:第 i 條邊的目標頂點。graph[v][2*i]:第 i 條邊的權重(持續時間)。ve[j] = max(ve[v] + w)。vl[v] = min(vl[j] - w),終點 vl = ve。(v, j) 滿足 ve[v] == vl[j] - w 即為關鍵活動。import java.util.Stack;
public class CriticalPath {
private int[][] graph;
private int[][] path;
private int len;
public void input(int[][] graph) {
this.graph = graph;
path = new int[graph.length - 1][3];
calculate();
}
private void calculate() {
int n = graph.length;
int[] ve = new int[n];
Stack st1 = new Stack<>(), st2 = new Stack<>();
// 拓撲排序 + 前向遞推
st1.push(0);
while (!st1.isEmpty()) {
int v = st1.pop();
for (int i = 1; i < graph[v].length; i += 2) {
int j = graph[v][i];
if (--graph[j][0] == 0) st1.push(j);
if (ve[v] + graph[v][i+1] > ve[j])
ve[j] = ve[v] + graph[v][i+1];
}
st2.push(v);
}
// 後向遞推
int[] vl = new int[n];
for (int i = 0; i < n; i++) vl[i] = Integer.MAX_VALUE;
vl[n-1] = ve[n-1];
while (!st2.isEmpty()) {
int v = st2.pop();
for (int i = 1; i < graph[v].length; i += 2) {
int j = graph[v][i];
if (vl[j] - graph[v][i+1] < vl[v])
vl[v] = vl[j] - graph[v][i+1];
}
}
// 關鍵邊收集
for (int v = 0; v < n-1; v++) {
for (int i = 1; i < graph[v].length; i += 2) {
int j = graph[v][i];
int w = graph[v][i+1];
if (ve[v] == vl[j] - w) {
path[len++] = new int[]{v, j, w};
}
}
}
}
public int[][] getPath() { return path; }
public int getLength() { return len; }
}
int[][] graph = {
{0, 1,6, 2,4, 3,5}, // 0→1(6) 0→2(4) 0→3(5)
{1, 4,1}, // 1→4(1)
{1, 4,1}, // 2→4(1)
{1, 5,2}, // 3→5(2)
{2, 6,9, 7,7}, // 4→6(9) 4→7(7)
{1, 7,4}, // 5→7(4)
{1, 8,2}, // 6→8(2)
{2, 8,4}, // 7→8(4)
{2} // 8 終點
};
// 輸出關鍵邊:
// 邊:0-1 權:6
// 邊:1-4 權:1
// 邊:4-6 權:9
// 邊:4-7 權:7
// 邊:6-8 權:2
// 邊:7-8 權:4
// 關鍵路徑:0-1-4-6-8 及 0-1-4-7-8
List 鄰接表降低空間複雜度。float = vl[j] - ve[v] - w 可進一步識別非關鍵活動的緩衝空間。