活動網路(Activity Network)將專案建模為**有向無環圖(DAG)**:
核心算法:**拓撲排序**(AOV 可行性驗證與執行順序)與 **關鍵路徑**(AOE 專案工期與瓶頸分析)。
template
class ActivityNetwork {
Network>* G; // 綁定鄰接表圖
int N; // 頂點數
vector criticalActivities; // 關鍵活動
int* count; // 入度陣列 (AOV) / 事件最早時間 (AOE)
int* result; // 拓撲序列 / 事件最晚時間
const int* out; // 輸出指標
struct CriAct { int s, d; CriAct() {} CriAct(int src, int dst) : s(src), d(dst) {} };
public:
ActivityNetwork(Network>* g)
: G(g), N(G->vNum()), out(result) {
count = new int[N]; result = new int[N];
}
~ActivityNetwork() { delete[] count; delete[] result; }
// 核心算法入口...
};
友元聲明**:需訪問 LinkedList 私有成員 vertices[i].e,故宣告為友元。
#define ITERATOR list::Edge>::iterator
#define BEGIN(i) G->data.vertices[i].e->begin()
#define END(i) G->data.vertices[i].e->end()
void initialize() {
fill_n(count, N, 0);
for (int i = 0; i < N; ++i)
for (ITERATOR iter = BEGIN(i); iter != END(i); ++iter)
++count[iter->vID]; // 統計入度
out = result;
}
bool topologicalSort() {
initialize();
queue q;
for (int i = 0; i < N; ++i)
if (count[i] == 0) q.push(i); // 入度 0 入隊
int sortedCount = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
result[sortedCount++] = u; // 輸出拓撲序列
for (ITERATOR iter = BEGIN(u); iter != END(u); ++iter) {
int v = iter->vID;
if (--count[v] == 0) q.push(v); // 入度歸零入隊
}
}
if (sortedCount != N) { // 存在環
cout << "Cycle detected! Not a valid AOV network." << endl;
return false;
}
out = result;
return true;
}
意義**:
| 符號 | 名稱 | 定義 |
|---|---|---|
| ve[i] | 事件最早發生時間 | 從源點出發最長路徑長度 |
| vl[i] | 事件最晚發生時間 | 不延遲專案完成的最晚時間 |
| e(a) | 活動最早開始時間 | e(a) = ve[尾部事件] |
| l(a) | 活動最晚開始時間 | l(a) = vl[頭部事件] - 活動持續時間 |
| slack(a) | 活動浮動時間 | l(a) - e(a),0 為關鍵活動 |
result[]ve[j] = max(ve[j], ve[i] + weight(i,j))vl[i] = min(vl[i], vl[j] - weight(i,j)),初始 vl[sink] = ve[sink]slack = 0 的邊構成關鍵路徑
void criticalPath() {
if (!topologicalSort()) return; // 必須是 DAG
int* ve = new int[N]; // 最早時間
int* vl = new int[N]; // 最晚時間
fill_n(ve, N, 0);
// 1. 正向:計算 ve
for (int k = 0; k < N; ++k) {
int u = result[k];
for (ITERATOR iter = BEGIN(u); iter != END(u); ++iter) {
int v = iter->vID;
if (ve[u] + iter->cost > ve[v])
ve[v] = ve[u] + iter->cost;
}
}
// 2. 反向:計算 vl
fill_n(vl, N, ve[result[N-1]]); // 汇點最晚 = 最早
for (int k = N-1; k >= 0; --k) {
int u = result[k];
for (ITERATOR iter = BEGIN(u); iter != END(u); ++iter) {
int v = iter->vID;
if (vl[v] - iter->cost < vl[u])
vl[u] = vl[v] - iter->cost;
}
}
// 3. 識別關鍵活動
criticalActivities.clear();
for (int u = 0; u < N; ++u) {
for (ITERATOR iter = BEGIN(u); iter != END(u); ++iter) {
int v = iter->vID;
int e = ve[u];
int l = vl[v] - iter->cost;
if (e == l) // 浮動時間為 0
criticalActivities.emplace_back(u, v);
}
}
delete[] ve; delete[] vl;
out = result; // 可輸出拓撲序列
}
| 場景 | 模型 | 核心輸出 |
|---|---|---|
| 專案排程管理 | AOE | 關鍵路徑、工期、浮動時間、資源平衡 |
| 編譯器指令排程 | AOE | 關鍵路徑長度 = 理論最小執行週期 |
| 建構系統依賴解析 | AOV | 建構順序、循環依賴檢測、平行建構層級 |
| 課程先修規劃 | AOV | 合法選課序列、畢業最短學期 |
| 晶片設計時序分析 | AOE | 關鍵路徑 = 最大時鐘頻率限制 |
標準 AOE 假設資源無限。現實中**資源有限**(機器、人員、預算),引入 **RCPSP (Resource-Constrained Project Scheduling Problem)**:
活動網路將專案管理數學化:**AOV 拓撲排序解決「能不能做、怎麼做順序」,AOE 關鍵路徑解決「多久做完、哪裡不能延」**。掌握 ve/vl 雙向動態規劃與浮動時間計算,是專案管理軟體、編譯器優化、硬體時序分析的核心算法素養。圖論應用六部曲完結,從儲存到演算法,從無向到有向,從靜態到動態,構建完整圖算法工程視野。