概述
1956年,美国杜邦公司提出关键路径法,并于1957年首先用于1000万美元化工厂建设,工期比原计划缩短了4个月。杜邦公司在采用关键路径法的一年中,节省了100万美元。
在项目管理中,关键路径是指网络终端元素的元素的序列,该序列具有最长的总工期并决定了整个项目的最短完成时间。关键路径的工期决定了整个项目的工期。任何关键路径上的终端元素的延迟将直接影响项目的预期完成时间(例如在关键路径上没有浮动时间)。
AOE网
AOE网(Activity On Edge)即边表示活动的网,是与AOV网(顶点表示活动)相对应的一个概念。而拓扑排序恰恰就是在AOV网上进行的,这是拓扑排序与关键路径最直观的联系。AOE网是一个带权的有向无环图,其中顶点表示事件(Event),弧表示活动,权表示活动持续的时间。下面的就是一个AOV网:
其中$V_{1},…,V_{9} $表示事件,a_{1},a_{2}…,a_{11} 表示活动,活动的取值表示完成该活动所需要的时间,如$a_{1}=6$表示完成活动$a_{1}$所需要的时间为6天。此外,每一事件$V_{i}$表示在它之前的活动已经完成,在它之后的活动可以开始,如$V_{5}$表示活动$a_{4}$和$a_{5}$已经完成,活动$a_{7}$和$a_{8}$可以开始了。
AOE网的源点和汇点
由于一个工程中只有一个开始点和一个完成点,故将AOE网中入度为零的点称为源点,将出度为零的点称为汇点。
关键路径
由于AOE网中的有些活动是可以并行进行的(如活动$a_{1}$、$a_{2}$和$a_{3}$就是可以并行进行的),所以完成工程的最短时间是从源点到汇点的最长路径的长度。路径长度最长的路径就叫做关键路径(Critical Path)。如下图中红色顶点和有向边构成的就是一条关键路径,关键路径的长度就是完成活动$a_{1}$、$a_{4}$和$a_{7}$、$a_{10}$所需要的时间总和,即为 6+1+9+2 = 18
ETV
ETV(Earliest Time Of Vertex):事件最早发生时间,就是顶点的最早发生时间。
事件$V_{2}$的最早发生时间表示从源点$V_{1}$出发到达顶点$V_{2}$经过的路径上的权值之和,从源点$V_{1}$出发到达顶点$V_{2}$只经过了权值为6的边,则$V_{2}$的最早发生时间为6,表示在活动$a_{1}$完成之后,事件$V_{2}$才可以开始;同理,事件$V_{6}$要发生(即最早发生)需要活动$a_{3}$和活动$a_{6}$完成之后才可以,故事件$V_{6}$的最早发生时间为 5 + 2 = 7。其他顶点(事件)的最早发生时间同理可的。需要说明,事件的最早发生时间一定是从源点到该顶点进行计算的.
LTV
LTV(Latest Time Of Vertex):事件最晚发生时间,就是每个顶点对应的事件最晚需要开始的时间,如果超出此时间将会延误整个工期。
上图中的关键路径($V_{1}$,$V_{2}$ ,$V_{5}$ ,$V_{7}$ ,$V_{9}$ )的长度为18,为什么要提这个长度呢,因为要计算某一个事件的最晚发生时间,我们需要从汇点进行倒推。计算顶点$V_{2}$的最晚发生时间为例,已知关键路径的长度为18,事件$V_{2}$到汇点$V_{9}$所需要的时间为 1 + 9 + 2 = 12,则事件$V_{2}$的最晚发生时间为18-12 = 6.再来计算一下事件$V_{6}$的最晚发生时间,事件$V_{6}$到汇点$V_{9}$所需要的时间为 4 + 4 = 8,则事件$V_{6}$的最晚发生时间为 18 - 8 = 10;相当于说活动$a_{6}$完成之后,大可以休息 3天,再去完成活动$a_{9}$也不会影响整个工期。
ETE
ETE(Earliest Time Of Edge):活动的最早开工时间,就是弧的最早发生时间。
活动$a_{4}$要最早开工时间为事件$V_{2}$的最早发生时间 6;同理,活动$a_{9}$的最早发生时间为事件$V_{6}$的最早发生时间 7。显然活动的最早开工时间就是活动发生前的事件的最早开始时间。
LTE
LTE(Lastest Time of Edge):活动的最晚发生时间,就是不推迟工期的最晚开工时间。
活动的最晚发生时间则是基于事件的最晚发生时间。比如活动$a_{4}$的最晚发生时间为事件$V_{5}$的最晚发生时间减去完成活动$a_{4}$所需时间,即 7 - 1 = 6;活动$a_{9}$的最晚发生时间为事件$V_{8}$的最晚发生时间减去完成活动$a_{4}$所需时间,即 14 - 4 = 10;
从上面也就可以看出 只要知道了每一个事件(顶点)的ETV 和 LTV,就可以推断出对应的 ETE 和 LTE . 此外还需要注意,关键路径是活动的集合,而不是事件的集合,所以当我们求得 ETV 和 LTV 之后,还需要计算 ETE 和 LTE 。
算法思想
求关键路径的过程事实上最重要的就是上面提到的四个概念,ETV、LTV、ETE 和 LTE,求得了ETE与LTE之后只需要判断两者是否相等,如果相等则为关键路径中的一条边,则输出。
首先通过拓扑排序获得每一个事件的最早发生时间:源点的最早发生时间肯定为0,其他节点的ETV也初始化为0.在拓扑排序的过程中,对于每一条边都有一个起点和终点,起点的ETV加上这条边上的活动的时间如果比终点的ETV大,则更新终点的ETV。
根据事件的最早发生时间ETV推断事件的最晚发生时间 LTV:初始时将LTV的值初始化为汇点的时间,然后根据上面求出的拓扑排序逆序遍历,对于每一条边的起点和终点,如果终点的ETV-活动的时间小于起点的LTV,则更新。
计算活动的最早与最晚发生时间:从源点$V_{1}$开始,遍历源点的邻接顶点$V_{2}$, 将弧 <$V_{1}$,$V_{2}$> 上的活动$a_{1}$的最早发生时间更新为源点$V_{1}$的最早发生时间 0,将活动$a_{1}$的最晚发生时间更新为事件$V_{2}$的最晚发生时间 6 减去活动$a_{1}$所需要的时间6,即为0. 判断活动$a_{1}$的最早发生时间与最晚发生时间是否相等,如果相等则为关键活动,并输出。同理遍历源点 的邻接顶点$V_{3}$和$V_{4}$,并更新弧$a_{2}$和$a_{3}$的最晚发生时间与最早发生时间。后续节点的遍历同理。
代码实现
输入格式:
第一行两个整数numVertexes,numEdges分别表示节点数和边数
接下来numEdges行每行三个整数x,y,z,表示从节点x到节点有一条边,该边上的活动所需时间为z
说明:节点编号从1开始
输出格式:
从源点开始每行以 c格式输出,表示关键路径中的一段从a到b,该段时间为c
样例输入:
9 11 |
样例输出:
<1,2> 6 |
C++实现:
|