若是在有向無環圖中用有向邊表示一個工程中的各項活動(Activity),用有向邊上的權值表示活動的持續時間(duration),用頂點表示事件(Event),則這種有向圖叫作用邊表示活動的網絡(activity on edges),簡稱AOE網絡。例如:node
其中,Ei表示事件,ak表示活動。E0是源點,E8是匯點。ios
完成整個工程所需的時間等於從源點到匯點的最長路徑長度,即該路徑中全部活動的持續時間之和最大。這條路徑稱爲關鍵路徑(critical path)。關鍵路徑上全部活動都是關鍵活動。所謂關鍵活動(critical activity),是不定期完成會影響整個工程進度的活動。只要找到關鍵活動,就能夠找到關鍵路徑。算法
與計算關鍵活動有關的量:數組
1 事件Ei的最先可能開始時間:Ee[i]—從源點E0到頂點Ei的最長路徑長度。在上圖中,Ee[4]=7。網絡
2 事件Ei的最遲容許開始時間:El(小寫L)[i]—在保證匯點En-1最遲容許開始時間El[n-1]等於整個工程所需時間的前提下,等於El[n-1]減去從Ei到En-1的最長路徑長度。數據結構
3 活動ak的最先可能開始時間:e[k]—設該活動在有向邊<Ei,Ej>上,從源點E0到頂點Ei的最長路徑長度,即等於Ee[i]。ide
4 活動ak的最遲容許開始時間:l(小寫L)[k]—設該活動在有向邊<Ei,Ej>上,在不會引發時間延誤的前提下,容許的最遲開始時間。l[k]=El[j]-dur(<Ei,Ej>),其中dur(<Ei,Ej>)是完成該活動所需的時間,即有向邊<Ei,Ej>的權值。spa
l[k]-e[k]表示活動ak的最先可能開始時間和最遲容許開始時間的時間餘量,也叫作鬆弛時間(slack time)。沒有時間餘量的活動是關鍵活動。.net
算法步驟:code
1 輸入頂點數和邊數,再輸入每條邊的起點編號、終點編號和權值。根據邊的信息,經過一維數組存儲每一個頂點的入度和出度,創建鄰接表保存邊。每一個頂點的Ee[i]設爲0,El[i]設爲100000000(表示無限大)。
2 按拓撲排序遍歷全部頂點,更新Ee[i]。若是拓撲排序循環次數小於頂點數n,則說明網絡中存在有向環,不能繼續求關鍵路徑。更新Ee[i]的最大值(整個工程所需時間)。
3 把全部匯點的最遲容許開始時間設爲整個工程所需時間,按逆拓撲排序遍歷全部頂點,更新El[i]。
4 經過DFS遍歷全部邊<Ei,Ej>。若是l[k]等於e[k],則說明該活動是關鍵活動。依次輸出關鍵活動上的頂點後構成關鍵路徑。
在上圖中,關鍵路徑是a1-a4-a7-a10或a1-a4-a8-a11,完成整個工程所需時間是18。
題目 PTA 數據結構與算法題目集(中文) 7-11 關鍵活動(30 分)
多源點和多匯點例子:
1 11 14 2 1 2 4 3 1 3 3 4 2 4 5 5 3 4 3 6 4 5 1 7 4 6 6 8 5 7 5 9 6 7 2 10 8 3 7 11 9 3 7 12 9 10 6 13 4 10 2 14 10 6 5 15 6 11 4
結果以下:
1 21 2 3->4 3 4->10 4 6->11 5 8->3 6 9->3 7 10->6
C++代碼以下:
1 //#include "stdafx.h" 2 #include <iostream> 3 #include <vector> 4 #include <queue> 5 6 using namespace std; 7 8 struct node 9 { 10 int next, time; 11 }; 12 13 int degree[2][110], t[2][110], maxtime; 14 vector<node> v[2][110]; 15 16 int torder(int index, int n) 17 { 18 int i; 19 queue<int> q; 20 21 for(i = 1; i <= n; i++) 22 { 23 if(degree[index][i] == 0) 24 { 25 if(index == 1) 26 { 27 t[1][i] = maxtime; 28 } 29 30 q.push(i); 31 } 32 } 33 34 int cur, size, next, nexttime[2], curtime, count = 0; 35 while(q.size() > 0) 36 { 37 cur = q.front(); 38 q.pop(); 39 40 count++; 41 42 size = v[index][cur].size(); 43 for(i = 0; i < size; i++) 44 { 45 next = v[index][cur][i].next; 46 degree[index][next]--; 47 48 if(degree[index][next] == 0) 49 { 50 q.push(next); 51 } 52 53 curtime = t[index][cur]; 54 nexttime[0] = curtime + v[index][cur][i].time; 55 nexttime[1] = curtime - v[index][cur][i].time; 56 57 if(index == 0 && nexttime[0] > t[0][next]) 58 { 59 t[0][next] = nexttime[0]; 60 } 61 else if(index == 1 && nexttime[1] < t[1][next]) 62 { 63 t[1][next] = nexttime[1]; 64 } 65 } 66 67 if(index == 0 && t[0][cur] > maxtime) 68 { 69 maxtime = t[0][cur]; 70 } 71 } 72 73 if(count < n) 74 { 75 return 0; 76 } 77 78 return 1; 79 } 80 81 int main() 82 { 83 int n, m; 84 scanf("%d%d", &n, &m); 85 86 int i, a, b; 87 node nod; 88 89 for(i = 1; i <= m; i++) 90 { 91 scanf("%d%d%d", &a, &b, &nod.time); 92 93 nod.next = b; 94 v[0][a].push_back(nod); 95 96 nod.next = a; 97 v[1][b].push_back(nod); 98 99 degree[1][a]++; 100 degree[0][b]++; 101 } 102 103 for(i = 1; i <= n; i++) 104 { 105 t[0][i] = 0; 106 t[1][i] = 100000000; 107 } 108 109 for(i = 0; i <= 1; i++) 110 { 111 if(torder(i, n) == 0) 112 { 113 printf("0\n"); 114 return 0; 115 } 116 } 117 118 printf("%d\n", maxtime); 119 120 int size, j, next; 121 for(i = 1; i <= n; i++) 122 { 123 size = v[0][i].size(); 124 for(j = size - 1; j >= 0; j--) 125 { 126 next = v[0][i][j].next; 127 if(t[1][next] - t[0][i] == v[0][i][j].time) 128 { 129 printf("%d->%d\n", i, next); 130 } 131 } 132 } 133 134 system("pause"); 135 return 0; 136 }
參考資料
《圖論算法理論、實現及應用》