圖——基本的圖演算法(四)關鍵路徑
圖——基本的圖演算法(四)關鍵路徑
1. 基本概念
(1)AOV網(Activity On Vertex Network)
AOV網是一個表示工程的有向圖中,其中的頂點用來表示活動,弧則用來表示活動之間的優先關係。舉個簡單的例子,假定起床後可以邊煮水,邊刷牙洗臉,但洗臉要在刷牙後,煮好水,刷好牙洗好臉以後,就可以喝水了,這個活動的AOV圖如下(其中的每個頂點都表示一個活動,而頂點之間的弧表示了活動之間的執行先後順序):
(2)AOE網( Activity On Edge Network)
AOE網是一個表示工程的帶權有向圖,其中的頂點用來表示某個事件,弧用來表示活動,弧上的權值用來表示活動持續的時間。例如上述例子的AOE網如下:
(3)AOE網和AOV網的區別
AOV網:其頂點用來表示活動。AOE網是用來表示活動之間的制約關係。
AOE網:頂點表示事件,邊表示活動,邊上的權值用來表示活動持續的時間。AOV網是用來分析工程至少需要花多少時間完成,或是為了縮短時間需要加快哪些活動等問題。
(4)源點、匯點、路徑長度
在AOE網中,
始點或源點:入度為0的頂點,它表示一個工程的開始;
終點或匯點:出度為0的頂點,它表示一個工程的結束;
路徑長度:路徑上各個活動所持續的時間之和。
(5)關鍵路徑、關鍵活動
在AOE網中,從源點到匯點具有最大長度的路徑稱為關鍵路徑,在關鍵路徑上的活動稱為關鍵活動。
2. 關鍵路徑演算法
2.1 基本思想
(1)要找出一個AOE網中的關鍵路徑,就要先找出網裡的關鍵活動,這些關鍵活動間的路徑就是關鍵路徑。
(2)判斷一個活動是不是關鍵活動,方法是要先找到所有活動的最早開始時間和最晚開始時間,並且對它們進行比較,如果二者相等(意味著這個活動在該工程中沒有時間鬆動),說明這個活動是關鍵活動。
(3)對於活動<Vi, Vj>,它最早的開始時間等於事件Vi最早的發生時間earlist[Vi](事件v的最早發生時間用earlist[v])。假設E[Vi]表示所有到達頂點Vi的弧的集合,len<Vi, Vj>表示活動<Vi, Vj>的持續時間,那麼:
注意,這裡假設頂點下標從0開始,所以Vi==0,則表示它是源點,因此最早的開始時間為0;當某個頂點不是源點,那麼只有在它前面的事件都發生完後,才能輪到這個事件,所以用了max。
(4)對於活動<Vi, Vj>,它最晚的開始時間等於事件Vj最晚的發生時間減去這個活動的持續事件,即latest[Vj]-len<Vi, Vj>(事件v的最晚的發生時間用latest[v])。假設S[Vj]表示所有從頂點Vj出發的弧的集合,len<Vj, Vk>表示活動<Vj, Vk>的持續時間,那麼:
2.2 演算法實現
(1)資料結構
typedef struct EdgeListNode{ //邊表結點
int adjId;
int weight;
EdgeListNode* next;
};
typedef struct VertexListNode{ //頂點表結點
int in; //入度
int data;
EdgeListNode* firstadj; //指向其邊表
};
typedef struct GraphAdjList{ //圖結構
int vertexnumber; //頂點個數
int edgenumber;
VertexListNode vertextlist[Maximum];
};
(2)具體實現
1)由基本思路可以知道,要求一個AOE網的關鍵路徑,步驟如下:
A. 首先初始化每個頂點的最早開始為0,然後對AOE網進行拓撲排序,在排序的過程中獲取每個頂點的最早開始時間;
B. 獲取拓撲排序後,初始化每個頂點的最晚開始時間為匯點的最早開始時間,並從AOE網的匯點開始,從後往前,對每個頂點找到求其最晚開始時間;
C. 遍歷圖中的每條邊(方法是遍歷圖中每個頂點的邊表),求其最早開始時間和最晚開始時間,如果二者相等,則這是個關鍵活動,將其加入關鍵路徑中。
2)程式碼
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<vector>
#include<sstream>
#include<list>
#include<stdlib.h>
#include<queue>
using namespace std;
#define Maximum 1000
typedef struct EdgeListNode{
int adjId;
int weight;
EdgeListNode* next;
};
typedef struct VertexListNode{
int in;
int data;
EdgeListNode* firstadj;
};
typedef struct GraphAdjList{
int vertexnumber;
int edgenumber;
VertexListNode vertextlist[Maximum];
};
//拓撲排序,返回拓撲排序結果,earlist儲存每個頂點的最早開始時間
vector<int> ToplogicalSort(GraphAdjList g, int *earlist) {
vector<int>v; //儲存拓撲排序結果
queue<int>q; //儲存入度為0的頂點
EdgeListNode *temp;
int i, j, k, ans;
ans = 0;
v.clear();
while(!q.empty()) {
q.pop();
}
//找出所有入度為0的頂點
for(i=1; i<=g.vertexnumber; i++) {
if(g.vertextlist[i].in == 0) {
q.push(i);
}
}
while(!q.empty()) {
i = q.front();
v.push_back(i);
q.pop();
ans++;
temp = g.vertextlist[i].firstadj;
while(temp != NULL) {
k = earlist[i] + temp->weight;
if(k > earlist[temp->adjId]) {
earlist[temp->adjId] = k;
}
j = --g.vertextlist[temp->adjId].in;
if(j == 0) {
q.push(temp->adjId);
}
temp = temp->next;
}
}
if(ans < g.vertexnumber) {
v.clear();
}
return v;
}
//求關鍵路徑,返回儲存關鍵路徑頂點的vector
vector<int> CriticalPath(GraphAdjList g) {
vector<int>ans;
vector<int>path;
int i, j, k, *earlist, *latest;
EdgeListNode *temp;
//earlist儲存每個頂點的最早開始時間
//latest儲存每個頂點的最晚開始時間
earlist = (int*)malloc(sizeof(int) * (g.vertexnumber+1));
latest = (int*)malloc(sizeof(int) * (g.vertexnumber+1));
path.clear();
for(i=1; i<=g.vertexnumber; i++) {
earlist[i] = 0;
}
ans = ToplogicalSort(g, earlist);
for(i=1; i<=g.vertexnumber; i++) {
latest[i] = earlist[ans[g.vertexnumber-1]];
}
for(i=g.vertexnumber; i>0; i--) {
temp = g.vertextlist[i].firstadj;
while(temp!=NULL) {
if(latest[temp->adjId] - temp->weight < latest[i]) {
latest[i] = latest[temp->adjId] - temp->weight;
}
temp = temp->next;
}
}
//遍歷每條邊
for(i=1; i<=g.vertexnumber; i++) {
temp = g.vertextlist[i].firstadj;
while(temp != NULL) {
j = earlist[i];
k = latest[temp->adjId] - temp->weight;
if(j == k) { //是關鍵活動
//把該活動的兩個頂點加入path
path.push_back(i);
path.push_back(temp->adjId);
}
temp = temp->next;
}
}
return path;
}
(3)測試
int main() {
GraphAdjList g;
EdgeListNode *e;
int i, j, k;
g.vertexnumber = 5;
g.edgenumber = 7;
for(i=1; i<=g.vertexnumber; i++) {
g.vertextlist[i].data = i;
g.vertextlist[i].firstadj = NULL;
}
g.vertextlist[1].in = 0;
g.vertextlist[2].in = 1;
g.vertextlist[3].in = 2;
g.vertextlist[4].in = 2;
g.vertextlist[5].in = 1;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 2; e->weight = 2; e->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = e;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 4; e->weight = 1; e->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = e;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 5; e->weight = 1; e->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = e;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 3; e->weight = 3; e->next = g.vertextlist[2].firstadj; g.vertextlist[2].firstadj = e;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 4; e->weight = 5; e->next = g.vertextlist[2].firstadj; g.vertextlist[2].firstadj = e;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 3; e->weight = 8; e->next = g.vertextlist[4].firstadj; g.vertextlist[4].firstadj = e;
e = (EdgeListNode*)malloc(sizeof(EdgeListNode));
e->adjId = 3; e->weight = 1; e->next = g.vertextlist[5].firstadj; g.vertextlist[5].firstadj = e;
vector<int>ans;
ans = CriticalPath(g);
for(i=0; i<ans.size(); i+=2) {
cout<<ans[i]<<"->"<<ans[i+1]<<endl;
}
cout<<endl;
return 0;
}