【关键路径】【拓扑排序+逆拓扑排序】【转】

https://blog.csdn.net/haskei/article/details/53749380

具体算法描述如下:
1. 输入e条弧<j,k>,建立AOE-网的存储结构。
2. 拓扑排序,并求得ve[]。从源点V0出发,令ve[0]=0,按拓扑有序求其余各顶点的最早发生时间ve[i]。如果得到的拓扑有序序列中顶点个数小于网中顶点数n,则说明网中存在环,不能求关键路径,算法终止;否则执行步骤3。
3. 拓扑逆序,求得vl[]。从汇点Vn出发,令vl[n-1] = ve[n-1],按逆拓扑有序求其余各顶点的最迟发生时间vl[i]。
4. 求得关键路径。根据各顶点的ve和vl值,求每条弧s的最早开始时间e(s)和最迟开始时间l(s)。若某条弧满足条件e(s) = l(s),则为关键活动。

为了能按逆序拓扑有序序列的顺序计算各个顶点的vl值,需记下在拓扑排序的过程中求得的拓扑有序序列,这就需要在拓扑排序算法中,增设一个栈,以记录拓扑有序序列,则在计算求得各顶点的ve值之后,从栈顶到栈底便为逆拓扑有序序列。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<string>
  6 #include<cstdlib>
  7 #include<stack> 
  8 
  9 using namespace std;
 10 
 11 typedef long long ll;
 12 const int maxm = 20;
 13 const int maxn = 100;
 14 const int inf = 0x3f3f3f3f;
 15 struct node {
 16     int x, y, w;
 17     int next;
 18 };
 19 node edge[maxm];
 20 int n, m;
 21 int head[maxn];
 22 //e代表活动开始的最早时间, l活动最迟开始的时间, ve[i]事件最早发生的时间, vl[i]事件最迟发生的时间 ,indegree[i]顶点的入度 
 23 //这个地方没必要分别为e,l开数组了,因为最后只是进行赋值,然后比较两个数是否相等而已,没必要开数组了就,不明白可以看下面的代码 
 24 int e, l, ve[maxn], vl[maxn], indegree[maxn];
 25 stack<int> s, t; //s代表逆序的拓扑排序 ,t代表入度为零的栈,里面存放入度为零的点 
 26 
 27 int TopologicalSort() {
 28     int i, cnt = 0;
 29     for(i=1; i<=n; ++i) //入度为零的点入栈 
 30         if(!indegree[i]) {
 31             t.push(i);
 32             ++cnt;
 33             //printf("%d ", i);
 34         }
 35     while(!t.empty()) {
 36         int a = t.top();
 37         s.push(a);
 38         //printf("%d ", a);
 39         t.pop();
 40         //去掉与入度为零的点的相连的边,对应的终点的入度减一 
 41         int k = head[a];
 42         while(k != -1) {
 43             if(!--indegree[edge[k].y]) {//终点的度减一后,如果度为零,入栈 
 44                 t.push(edge[k].y);
 45                 ++cnt;
 46                 //printf("%d ", edge[k].y);
 47             }
 48             if(ve[edge[k].y] < ve[a] + edge[k].w) //正拓扑排序求事件发生的最早时间ve[i],到edge[k].y的最长路径 
 49                 ve[edge[k].y] = ve[a] + edge[k].w;
 50             k = edge[k].next;    
 51         }
 52     }
 53     if(cnt < n) 
 54         return 0;
 55     return 1;
 56 }
 57 
 58 
 59 int main()
 60 {
 61     int i;
 62     memset(head, -1, sizeof(head));
 63     scanf("%d%d", &n, &m);
 64     
 65     //建立邻接表 
 66     for(i=1; i<=m; ++i) {
 67         scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].w);
 68         ++indegree[edge[i].y];  //终点的入度加一 
 69         edge[i].next = head[edge[i].x];
 70         head[edge[i].x] = i;
 71     }
 72     
 73     if(TopologicalSort() == 0) { //在 TopologicalSort()函数里面已经解决了ve的问题 
 74         printf("不存在关键路径,存在环\n");
 75         return 0;
 76     }
 77     
 78     memset(vl, inf, sizeof(vl));
 79     vl[n] = ve[n]; //最后一个事件的最迟发生事件就等于最早发生时间,因为是最后一件事,也就是说这个工程干完了,以后就没有事情做了 
 80     while(!s.empty()) { //逆拓扑排序求vl[i] 
 81         int a = s.top();
 82         s.pop();
 83         int k = head[a];
 84         while(k != -1) {
 85             if(vl[a] > vl[edge[k].y] - edge[k].w) {
 86                 vl[a] = vl[edge[k].y] - edge[k].w;
 87             }
 88             k = edge[k].next;
 89         }
 90     }
 91     printf("\n关键活动(该活动不能推迟开工)有:\n");
 92     for(i=1; i<=n; ++i)  { 
 93         int k = head[i];
 94         while(k != -1) {
 95             e = ve[i]; //该条边的起点代表事情,该条边表示的活动的最早发生时间就等于起点代表的事情的最早发生时间 
 96             //活动的最迟发生时间
 97             l = vl[edge[k].y] - edge[k].w;
 98             if(l == e)
 99                 printf("%d %d %d\n", i, edge[k].y, edge[k].w);
100             k = edge[k].next;
101         }
102     }
103     return 0;
104 }
105 /*
106 9 11
107 1 2 6
108 1 3 4
109 1 4 5
110 2 5 1
111 3 5 1
112 4 6 2
113 5 7 9
114 5 8 7
115 6 8 4
116 7 9 2
117 8 9 4
118 */

 

posted @ 2018-05-07 22:40  MekakuCityActor  阅读(4853)  评论(0编辑  收藏  举报