关键路径

来源:互联网 发布:上古卷轴5捏脸数据导入 编辑:程序博客网 时间:2024/06/08 12:05

一、AOE网的概念


        与上一文AOV网相对应的是AOE网,即边表示活动的网络。它与AOV网比较,更具有使用价值,通常用它表示一个工程的计划或进度。

       AOE网是一个有向带权图,图中的边表示活动(子工程),边上的权表示活动的持续时间,即完成该活动所需要的时间;图中的顶点表示事件,每个事件是活动之间的转接点,即表示它的所有入边活动到此完成,所有出边活动从此开始。AOE网中有两个特殊的顶点(事件):一个称做源点,它表示整个工程的开始,亦即最早活动的起点,显然

它只用出边,没有;另一个称为汇点,它表示整个工程的结束,亦即最后活动的终点,显然它只有入边,没有出边。除这两个顶点外,其余顶点都既有入边,也有出边,是入边活动和出边活动的转接点。在一个AOE网中,若包含n个时间,通常另源点为第0个事件(假定从0开始编号),汇点为第n-1个事件,其余事件的编号(即顶点序号)分别从1至n-2。

       图9-15就是一个AOE网,该网中包含11项活动和9个事件。例如,边<0,1>表示活动a1,持续时间(即权值)为6,假定以天为单位,即a1需要6天完成,它以V0事件为起点,以V1事件为终点;边<4,6>和<4,7>分别表示活动a7和a8,它们的持续时间分别为9天和7天,它们均以V4事件为起点,但分别以V6和V7时间为终点。该网中 的源点和汇点分别为第0个事件V0和最后一个时间V8,它们分别表示整个工程的开始和结束。




二、时间的最早发生时间


        在AOE网中,一个顶点事件发生或出现必须在它的所有入边活动(或称前驱活动)都完成之后,也就是说,只要有一个入边活动都没完成,该事件就不可能发生。显然,一个时间的最早发生时间是它的所有入边活动,或者说最后一个入边活动刚完成的时间。同样,一个活动的开始必须在它的起点事件发生之后,也就是说,一个顶点事件没有发生时,它的所有出边活动(或称后继活动)都不可能开始。显然一个活动的最早开始时间是它的起点事件的最早发生时间。

        一个事件的发生有待于它的所有入边活动的全部完成,而每个入边活动的开始和完成又有待于前驱事件的发生,而每个前驱事件的发生又有待于它们的所有入边活动的完成,.......总之,一个事件发生在从源点到该顶点的所有路径上的活动都完成之后,显然,其最早发生时间应等于从源点到该顶点的所有路径上的最长路径长度。这里所说的路径长度是指带权路径长度,即等于路径上所有活动的持续时间之和。例如,从源点V0到顶点V4共有两条路径,长度分别为7和5,所以V4的最早发生时间为7。从源点V0到汇点V8有多条路径,通过分析可知,其最长路径长度为18,所以汇点V8的最早发生时间为18。汇点事件发生表明整个工程的所有活动都已完成,所以完成图9-15所对应的工程至少需要18天。

        现在接着讨论如何从源点V0的最早发生时间0出发,求出其余各事件的最早发生时间,求一个时间Vk的最早发生时间(即从源点V0到Vk的最长路径长度)的常用方法是:由它的每个前驱事件Vj的最早发生时间(即从源点V0到Vj的最长路径长度)分别加上相应入边<j,k>上的权,其值最大者就是Vk的最早发生时间。由此可知,必须按照拓扑序列中的顶点次序(即拓扑有序)求出各个事件的最早发生时间,这样才能保证在求一个事件的最早发生时间,它的所有前驱事件的最早发生时间都已求出。

       设ve[k]表示Vk事件的最早发生时间,ve[j]表示Vk的一个前驱事件Vj的最早发生时间,dut(<j,k>)表示边<j,k>上的权,p表示Vk顶点所有入边的集合,则AOE网中每个事件Vk(0<=k<n-1)的最早发生时间可由下式,按照拓扑有序计算出来。ve[k]=max{ ve[j]+dut(<j,k>)}         (1<=k<=n-1,<j,k>属于p,ve[0]=0)


三、事件的最迟发生时间


         在不影响整个工程按时完成的前提下,一些事情可以不在最早发生时间发生,而允许向后推迟一些时间发生,把最晚必须发生的时间叫做该事件的最迟发生时间。同样,在不影响整个工程按时完成的前提下,一些活动可以不在最早开始时间开始,而允许向后推迟一些时间开始,把最晚必须开始的时间叫做该活动的最迟开始时间。 AOE网中任一个事件若在最迟发生时间仍没有发生或任一项活动在最迟开始时间仍没有开始,则必将影响整个工程的按时完成,使工期拖延。若用Vl[k]表示顶点Vk事件的最迟发生时间,用l[i]表示Vk的一条入边<j,k>上活动ai最迟开始时间,用dut(<j,k>)表示ai的持续时间,则有:l[i]=vl[k]-dut(<j,k>)

        因ai活动的最迟完成时间也就是它的终点事件Vk的最迟发生时间,所以ai的最迟开始时间应等于Vk的最迟发生时间减去ai的持续时间,或者说,要比Vk的最迟发生时间提前ai所需的时间开始。

        为了保证整个工程的按时完成,所以把汇点的最迟发生时间定义为它的最早发生时间,即vl[n-1]=ve[n-1]。其他每个事件的最迟发生时间应等于汇点的最迟发生时间减去从该事件的顶点到汇点的最长路径长度,或者说,每个事件的最迟发生时间比汇点的最迟发生时间所提前的时间应等于从该事件的顶点到汇点的最长路径上所有活动的持续时间之和。求一个事件Vj的最迟发生时间常用方法是:由它的每个后继时间Vk的最迟发生时间分别减去相应出边<j,k>上的权,其值最小者就是Vj的最迟发生时间。由此可知,必须按照它的所有后继事件的最迟发生时间都已求出。设vl[j]表示待求的Vj事件的最迟发生时间,vl[k]表示Vj的一个后继时间Vk的最迟发生时间,dut(<j,k>)表示边<j,k>上的权,s表示Vj顶点的所有出边的集合,则AOE网中每个事件Vj(0<=j<=n-1)的最迟发生时间与下式,按照逆拓扑有序计算出来:

              ve[n-1]     (j=n-1)

      vl[j]=

               min{ vl[k]-dut<j,k>}                 (0<=j<=2,<j,k>属于s)


四、活动的最早和最迟开始时间


        AOE网中每个事件的最早发生时间和最迟发生时间计算出来后,可根据他们计算出每个活动的最早开始时间和最迟开始时间。设事件vj的最早发生时间为ve[j],它的一个后继事件vk的最迟发生时间为vl[k],则边<j,k>上的活动ai的最早开始时间e[i]和最迟开始时间l[i]的计算公式重新列出如下:

              e[i]=ve[j]

              l[i]=vl[k]-dut(<j,k>) 

        根据此计算公式可计算出AOE网中每一个活动ai的最早开始时间e[i]、最迟开始时间l[i]和开始时间余量l[i]-e[i]。


五、关键路径和关键活动


         在图9-16中,有些活动的开始时间余量不为0,表明这些活动不在最早开始时间开始,至多向后拖延相应的开始时间余量所规定的时间开始也不会延误整个工程的进展。例如对于活动a5,它最早可以从整个工程开工后的第4天开始,至多向后拖延两天,即从第6天开始。有些活动的开始时间余量为0,表明这些活动只能在最早时间开始,并且必须在持续时间内完成,否则将拖延整个工期。把开始时间余量为0的活动称为关键活动,由关键活动所形成的从源点到汇点的每一条路径称为关键路径。图9-15中的关键路径只有一条,即为{0,1,4,6,8},如图9-17所示。


        关键路径实际上机就是从源点到汇点具有最长路径长度的那些路径,即最长路径。这很容易理解,因为整个工程的工期就是按照最长路径长度计算出来的,即等于该路径上所有活动的持续时间之和。当然一条路径上的活动只能串行进行,若最长路径上的任一活动不在最早开始时间开始,或不在规定的持续时间内完成,都必然会延误整个工期,所以每一项活动的开始时间余量为0,故它们都是关键活动。

       求出一个AOE网的关键路径后,可通过加快关键活动(即缩短它的持续时间)来实现缩短整个工程的工期。但当同时存在多条关键路径时,并不是加快任何一个关键活动都可以缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到这个目的。还有,关键路径是可以变化的,提高某些关键活动的速度可能使原来的非关键路径变为新的关键路径,因而关键活动的速度提高是有限度的。例如,图9-15中关键活动a1由6改为4后,路径{0,2,4,6,8}也变成了关键路径,此时,再提高a1的速度也不能使整个工程的工期提前。


6、求AOE网中关键路径的算法描述

    

        假定一个AOE网用邻接表类的对象gr表示,下面是求其关键路径的算法描述。

//求AOE网中关键路径的算法描述public static void criticalPath(LinkedGraph gr,int[][] cc){//求用邻接表类对象gr表示的AOE网中的关键路径,相关数据存于数组cc中//定义相关的变量,其中,用n表示顶点数,数组b表示邻接表的表头向量int i,j,k,m=0,n=gr.vertices();EdgeNode p,b[]=gr.getArray();//定义具有n个元素的3个一维整型数组v,ve和vlint [] v=new int [n];              //用v保存拓扑排序的顶点序列int [] ve=new int[n];              //用ve保存每个事件的最早发生时间int [] vl=new int[n];              //用vl保存每个事件的最迟发生时间//调用拓扑排序算法,使排序结果存于数组v中if(TopoSort(gr,v)==false){return;}//给每个事件的最早发生时间置初值0for(i=0;i<n;i++){ve[i]=0;}for(i=0;i<n;i++){j=v[i];p=b[j];while(p!=null){k=p.adjvex;if(ve[k]<ve[j]+p.weight){ve[k]=ve[j]+p.weight;p=p.next;}}}//把每个事件的最迟发生时间都置为ve[n-1]的值,以作为它们的初值for(i=0;i<n;i++){vl[i]=ve[n-1];}//求出每个事件的最迟发生时间for(i=n-1;i>=0;i--){j=v[i];p=b[j];while(p!=null){k=p.adjvex;if(vl[j]>vl[k]-p.weight){vl[j]=vl[k]-p.weight;p=p.next;}}}//计算每项活动的最早开始时间,最迟开始时间,以及开始时间余量for(i=0;i<n;i++)                        //依次扫描邻接表中的每个边结点{p=b[i];                             //把vi邻接点表的表头指针赋给pwhile(p!=null){j=p.adjvex;                     //Vj是vi的一个后继顶点事件cc[m][0]=i;                     //保存有向边<i,j>的起止序号cc[m][1]=j;              cc[m][2]=p.weight;              //保存<i,j>上的权值cc[m][3]=ve[i];                 //保存<i,j>上活动的最早开始时间cc[m][4]=vl[j]-p.weight;        //保存<i,j>上活动的开始时间余量cc[m][5]=vl[j]-p.weight-ve[i];  //保存<i,j>上活动的开始时间余量m++;                            //下标m值增1p=p.next;                       //使p指向vi顶点的下一个边结点}}}


            






原创粉丝点击