求AOE网的关键路径

转自http://blog.csdn.net/jkay_wong/article/details/6696701

以边表示活动,以顶点表示事件的有向网称为AOE(activity on edge)网.AOE网是一个
有向无环图,权值表示活动持续的时间。可以用AOE网来估计工程完成的时间。由于工程
只有一个开始点和一个完成点,所以在无环路的条件下,网中只有一个入度为0的点和一
个出度为0的点.
下面是几个和AOE网有关的概念:
(1)路径长度:路径上各个活动的持续时间之和

(2)完成工程的最短时间:由于AOE网中有活动是并行进行的,所以完成工程的最短时间
就是从开始点到完成点的最长路劲长度。
(3)活动最早开始时间(earlist time)(e(i)):从开始点到顶点vi的最长路径称为事件vi的最早发生时间,

这个时间决定了以vi为尾的弧表示的活动的最早开始时间.
(4)活动最晚开始时间(latest time)(l(i)):在不推迟整个工程完成的前提下,活动最迟开始的时间
(5)完成活动的时间余量:该活动的最迟开始时间减去最早开始时间
(6)关键路径(critical path):路径长度最长的路径称为关键路径
(7)关键活动(critical activity):关键路径上的活动称为关键活动,关键活动的特点是:e(i)=l(i)
分析关键路径的目的就是辨别在整个工程中哪些是关键活动,以便争取提高关键活动的工作
效率,缩短整个工程的工期。

文件"aoe.h"

  1. #include<iostream>  
  2. #include<string>  
  3. #include<stack>  
  4. #include<iomanip>  
  5. using namespace std;  
  6.   
  7. const int MAX_VEX_NUM=20;  
  8. int ve[20];//全局变量,存放各个事件的最早发生时间  
  9.   
  10. class ArcNode //表结点  
  11. {  
  12. public:  
  13.     int adjvex;  
  14.     int info;//权值  
  15.     ArcNode *nextarc;  
  16. };  
  17.   
  18. class VNode //头结点  
  19. {  
  20. public:  
  21.     string data;  
  22.     int indegree; //顶点的入度  
  23.     ArcNode *firstarc;  
  24. };  
  25.   
  26. class ALGraph   
  27. {  
  28. private:  
  29.     VNode vertices[MAX_VEX_NUM];  
  30.     int arcnum;  
  31.     int vexnum;  
  32. public:  
  33.   
  34.     void Create_ALG()  
  35.     {  
  36.         //构造有向网       
  37.         string v1,v2;  
  38.         int i,j,w;  
  39.         ArcNode *p=NULL;  
  40.   
  41.         cout<<"输入顶点数和边数:";  
  42.         cin>>vexnum>>arcnum;  
  43.   
  44.         cout<<"输入顶点名称:";  
  45.         for(i=0;i<vexnum;i++)  
  46.         {  
  47.             cin>>vertices[i].data;  
  48.             vertices[i].firstarc=NULL;  
  49.             vertices[i].indegree=0;  
  50.         }  
  51.   
  52.         for(int k=0;k<arcnum;k++)  
  53.         {  
  54.             cout<<"按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:";  
  55.             cin>>v1>>v2>>w;  
  56.             i=Locate_Vex(v1);  
  57.             j=Locate_Vex(v2);  
  58.   
  59.             while(i==-1 || j==-1)  
  60.             {  
  61.                 cout<<"输入的顶点错误,重新输入:";  
  62.                 cin>>v1>>v2;  
  63.                 i=Locate_Vex(v1);  
  64.                 j=Locate_Vex(v2);  
  65.             }  
  66.   
  67.             p=new ArcNode;  
  68.             p->adjvex=j;  
  69.             p->info=w;  
  70.             p->nextarc=vertices[i].firstarc;  
  71.             vertices[i].firstarc=p;  
  72.             vertices[j].indegree+=1; //作为有向弧的头的顶点入度加1  
  73.         }  
  74.   
  75.         cout<<"有向网构造完成"<<endl;  
  76.     }  
  77.   
  78.     int Locate_Vex(string v) //求顶点在顶点数组中的位置  
  79.     {  
  80.         for(int k=0;k<vexnum && vertices[k].data!=v;k++);  
  81.         if(k<vexnum)  
  82.             return k;  
  83.         else  
  84.             return -1;  
  85.     }  
  86.                                 /*关键路径求解思想*/  
  87.             /*--------------------------------------------------------------- 
  88.             / 辨别关键路径就是要找出l(i)=e(i)的活动,为了求AOE网中的e(i)和l(i) 
  89.             / 首先应该求出每个事件的最早发生时间ve(j)和最晚发生时间vl(j),如果 
  90.             / 活动ai用弧<j,k>表示,那么持续时间记为dut(<j,k>).则有如下关系: 
  91.             / e(i)=ve(j),   l(i)=vl(k)-dut(<j,k>); 
  92.             / 求事件的vj的最早发生时间ve(j)和最迟发生时间vl(j)要分两步进行: 
  93.             / (1):从ve(0)=0(假设顶点0是开始点)开始,根据下面公式计算其它事件 
  94.             / 的最早开始时间: ve(j)=Max{ve(i)+dut(<i,j>)} 其中i是j的所有直接前驱的集合 
  95.             / (2)从vl(n-1)=ve(n-1)开始,根据下面公式计算其他事件的最晚开始时间: 
  96.             / vl(i)=Min{vl(j)-dut(<i,j>)} 其中j是i的直接后继的集合 
  97.             / 上述两个公式必须分别在拓扑有序和逆拓扑有序的前提下进行,也就是说ve(j-1) 
  98.             / 必须在vj全部直接前驱的最早发生时间都求得以后才能确定。而vl(j-1)则必须在 
  99.             / vj的所有直接后继的最晚发生时间求得之后才能确定,因此可以在拓扑排序的基础 
  100.             / 上计算所有事件的ve(j-1)和vl(j-1). 
  101.             / 为了能按逆拓扑有序序列的顺序计算各个顶点的vl值,需记下在拓扑排序的过程中 
  102.             / 求得的拓扑有序序列,这只需要增加多一个栈,用来存储拓扑有序序列即可. 
  103.             / 由于栈的结构特点,拓扑有序序列出栈就变成逆拓扑有序序列了. 
  104.             /----------------------------------------------------------------*/  
  105.   
  106.     //求所有事件的最早发生时间  
  107.     bool Topo_Order(stack<int> &T)  
  108.     {  
  109.         stack<int> s;  
  110.         ArcNode *p=NULL;  
  111.         for(int i=0;i<vexnum;i++)  
  112.             if(!vertices[i].indegree)  
  113.                 s.push(i);  
  114.         int count=0;  
  115.         for(i=0;i<vexnum;i++)  
  116.             ve[i]=0; //设各顶点最早发生为0  
  117.         while(!s.empty())  
  118.         {  
  119.             int k=s.top();  
  120.             s.pop();  
  121.             T.push(k);  
  122.             count++;  
  123.             for(p=vertices[k].firstarc;p;p=p->nextarc)  
  124.             {  
  125.                 int w=p->adjvex;  
  126.                 if(vertices[w].indegree)  
  127.                     vertices[w].indegree--;  
  128.                 if(!vertices[w].indegree)  
  129.                     s.push(w);  
  130.                 if(ve[k]+p->info>ve[w]) //求ve[w]的最早发生时间  
  131.                     ve[w]=ve[k]+p->info;  
  132.             }  
  133.         }  
  134.         if(count<vexnum)  
  135.             return 0;  
  136.         else  
  137.             return 1;  
  138.     }  
  139.       
  140.     //求所有事件的最晚发生时间并求出关键活动和关键路径  
  141.     void Critical_Path()  
  142.     {  
  143.         stack<int> T;  
  144.         string cp[10];  
  145.         int c=0;  
  146.         if(!Topo_Order(T))  
  147.         {  
  148.             cout<<"该有向网有环!"<<endl;  
  149.             return;  
  150.         }  
  151.         int vl[20];  
  152.         for(int i=0;i<vexnum;i++)  
  153.             vl[i]=ve[vexnum-1]; //初始化顶点事件最迟发生时间  
  154.           
  155.         while(!T.empty())  
  156.         {  
  157.             ArcNode *p=NULL;  
  158.             int j=T.top();  
  159.             T.pop();  
  160.             for(p=vertices[j].firstarc;p;p=p->nextarc)  
  161.             {  
  162.                 int k=p->adjvex;  
  163.                 int dut=p->info;  
  164.                 if(vl[k]-dut<vl[j])  
  165.                     vl[j]=vl[k]-dut;  
  166.             }  
  167.         }  
  168.         //下面是根据关键活动的特点(最早开始时间和最晚开始时间相等)求关键活动,做上标记  
  169.         cout<<"tail  head  weight  earliest time  latest time  tag "<<endl;  
  170.         for(int j=0;j<vexnum;j++)  
  171.             for(ArcNode *p=vertices[j].firstarc;p;p=p->nextarc)  
  172.             {  
  173.                 int k=p->adjvex;  
  174.                 int dut=p->info;  
  175.                 int ee=ve[j];  
  176.                 int el=vl[k]-dut;  
  177.                 char tag;  
  178.                 tag=(ee==el)?'*':' ';  
  179.                 cout<<setw(3)<<vertices[j].data<<setw(6)<<vertices[k].data<<setw(7)<<dut<<setw(9)<<ee<<setw(16)<<el<<setw(9)<<tag<<endl;  
  180.                 if(tag=='*' && j==0)  
  181.                 {  
  182.                     cp[c]=vertices[j].data;  
  183.                     c++;  
  184.                     cp[c]=vertices[k].data;  
  185.                     c++;  
  186.                 }  
  187.                 else if(tag=='*' && j!=0)  
  188.                 {  
  189.                     cp[c]=vertices[k].data;  
  190.                     c++;  
  191.                 }             
  192.             }  
  193.         cout<<"The critical activities are ended with * "<<endl;  
  194.         cout<<"So the critical path is :";  
  195.         for(i=0;i<c-1;i++)  
  196.             cout<<cp[i]<<"->";  
  197.         cout<<cp[c-1]<<endl;  
  198.     }  
  199.   
  200. };  

主函数"main.cpp"

  1. #include"aoe.h"  
  2.   
  3. int main()  
  4. {  
  5.     ALGraph G;  
  6.     G.Create_ALG();  
  7.     G.Critical_Path();  
  8.     cout<<endl;  
  9.     return 0;  
  10. }  

输入和输出结果:

  1. 输入顶点数和边数:6 8  
  2. 输入顶点名称:v1 v2 v3 v4 v5 v6  
  3. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v1 v2 3  
  4. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v1 v3 2  
  5. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v2 v5 3  
  6. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v2 v4 2  
  7. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v3 v4 4  
  8. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v3 v6 3  
  9. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v4 v6 2  
  10. 按照尾->头的顺序输入每条边对应的两个顶点和该边的权值:v5 v6 1  
  11. 有向网构造完成  
  12. tail  head  weight  earliest time  latest time  tag  
  13.  v1    v3      2        0               0        *  
  14.  v1    v2      3        0               1  
  15.  v2    v4      2        3               4  
  16.  v2    v5      3        3               4  
  17.  v3    v6      3        2               5  
  18.  v3    v4      4        2               2        *  
  19.  v4    v6      2        6               6        *  
  20.  v5    v6      1        6               7  
  21. The critical activities are ended with *  
  22. So the critical path is :v1->v3->v4->v6  
  23.   
  24. Press any key to continue  

根据输入所生成的有向网如下所示:

当关键路径只有一条时,输出关键路径是对的,当关键路径不知一条时就是错的,但是依然是可以找出所有的关键活动,路径输出的算法,以后会完善到可以输出所以关键路径

求逆拓扑有序序列 除了利用求拓扑有序时进栈外,还可以直接用DFS遍历该有向图,直到该结点的所有邻接结点都输出之后,才将其输出,这个序列就是逆拓扑有序序列


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值