關鍵路徑算法(CPM)


一、關鍵路徑算法

  關鍵路徑算法是在工程能完成的情況下找出關鍵的幾個活動,達到工程更早完成的效果。

二、算法分析

  這個算法中我們需要用到etv,ltv,ete,lte這幾個變量,還需要用到確保工程能完成的算法,也就是拓撲排序算法

  在這個算法中我們把頂點看作是事件,邊權看作是活動持續時間,邊看作是活動

  解釋一下這幾個變量的含義,etv,ltv(即事件最早開始時間和時間最晚開始時間)

               ete,lte(即活動最早開始時間和活動最晚開始時間)

三、算法步驟

  1.利用拓撲排序求出是否能進行關鍵路徑的查詢

  2.在拓撲排序的過程中求出事件的最早開始時間

  3.利用棧把拓撲序列存儲下來,然后從棧頂依次推出事件的最晚開始時間

  4.利用ete和lte判斷這個活動是否是關鍵活動

  5.ete等於這個事件的最早開始時間(為什么?因為你這個事件要想發生,必須等前面的事情全部發生完,所以就是最早開始時間的求解步驟)

  6.lte等於這個事件的下一個事件的最晚開始時間減去持續時間(為什么?因為下一個事件的最晚開始時間代表着后面的事件能否正常進行,也就是意味着后面的活動是否能進行,同理,最晚活動開始時間也因該是下一個事件的最晚開始時間-持續時間)

四、代碼實現

 1 #include "bits/stdc++.h"
 2 using namespace std;
 3 int etv[110],ltv[110];//etv是事件最早開始事件,ltv是事件最晚開始事件
 4 int indegree[110];//記錄某個頂點的入度
 5 int u[110],v[110],w[110];//建立鄰接表
 6 int first[110],outnext[110];
 7 stack <int> Topo;
 8 int n,m;
 9 void TopologicalSort()//進行拓撲排序
10 {
11     int cnt = 0;
12     queue <int> ans;
13     for(int i = 1;i <= n;i++)
14         if(!indegree[i])//把入度為0的點放入隊列
15             ans.push(i);
16     while(!ans.empty()){
17         int v1 = ans.front();
18         ans.pop();
19         Topo.push(v1);//把拓撲排序后的序列放入棧,為后面求解最遲事件開始事件做鋪墊
20         cnt++;
21         int k =  first[v1];//把他的出邊全部遍歷
22         while(k != -1){
23             if(!(--indegree[u[k]]))//統計入度為0的點加入拓撲序列
24                 ans.push(u[k]);
25             etv[u[k]] = max(etv[u[k]],etv[v1] + w[k]);//求解事件最早開始時間,為什么需要最大值呢?因為你需要保證前面的事件全部都已經結束,所以你這件事只能在前面幾個用時最長的時間做統計
26             k = outnext[k];
27         }
28     }
29     if(cnt < n)//判斷是否為拓撲序列
30         cout << "Fail" << endl;
31     else 
32         cout << "Successful!" << endl;
33 }
34 void CriticalPathMethod()
35 {   
36     TopologicalSort();//進行拓撲排序
37     for(int i = 1;i <= n;i++)//因為ltv是求最小值,那么這個數組中的最大也一定是etv中的最大,這樣也不會影響ltv的值的求解
38         ltv[i] = etv[n];
39     while(!Topo.empty()){//從匯點(終點)往源點(起點)進行求解ltv(最遲開始時間)
40         int v1 = Topo.top();
41         Topo.pop();
42         int k = first[v1];
43         while(k != -1){
44             ltv[v[k]] = min(ltv[v[k]],ltv[u[k]] - w[k]);//為什么要用min呢?因為你必須得讓后面的活動都能夠按正常時間進行,所以你不能用最大的,不然后面的工程可能會受到影響
45             k = outnext[k];
46         }
47     }
48     int ete,lte;//ete是活動最早開始時間,lte是活動最晚開始時間。(這里活動指的是邊,而事件指的是頂點)
49     for(int i = 1;i <= n;i++){
50         int k = first[i];
51         while(k != -1){
52             ete = etv[i];//活動最早開始時間就是這之前最長的路徑,所以也就是事件最早開始時間
53             lte = ltv[u[k]] - w[k];//活動最晚開始時間就是不推遲工期的最晚開工時間,也就是出邊的事件的最晚發生時間-這個持續時間(也就是邊權)
54             if(lte == ete)//當最早活動開始時間等於最晚活動開始時間時,這個活動就是關鍵活動,而關鍵活動的全集就是關鍵路徑,關鍵路徑有可能不止一條
55                 cout << "(" << i << ',' << u[k] << ')' << ":weight" << w[k] << endl;
56             k = outnext[k];
57         }
58     }
59 }
60 int main()
61 {
62     cin >> n >> m;
63     for(int i = 1;i <= n;i++)
64         first[i] = -1;
65     for(int i = 1;i <= m;i++){
66         cin >> v[i] >> u[i] >> w[i];
67         indegree[u[i]]++;//入度+1
68         outnext[i] = first[v[i]];
69         first[v[i]] = i;
70    }
71     CriticalPathMethod();
72     return 0;
73 }

 五、測試數據

test case1 

6 8
1 2 3
1 3 2
2 5 3
2 4 2
3 6 3
4 6 2
5 6 1
3 4 4

answer1

 

 

 

test case2:

10 13
1 2 3
1 3 4
2 4 5
3 4 8
2 5 6
3 6 7
4 5 3
5 8 4
6 8 6
8 9 5
5 7 9
7 10 2
9 10 3

answer2

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM