前面我们说过的拓扑排序主要是为解决一个工程能否顺序进行的问题,但有时我们还需要解决工程完成需要的最短时间问题。如果我们要对一个流程图获得最短时间,就必须要分析它们的拓扑关系,并且找到当中最关键的流程,这个流程的时间就是最短时间。

在前面讲了AOV网的基础上,来介绍一个新的概念。在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,称之为AOE网(Activity On edge Network)。由于一个工程,总有一个开始,一个结束,在正常情况下,AOE网只有一个源点一个汇点。

既然AOE网是表示工程流程的,所以就具有明显的工程属性。只有在某顶点代表的事件发生后,从该顶点出发的各活动才能开始。只有在进入某顶点的各活动都已经结束,该顶点代表的事件才能发生。

尽管AOV网和AOE网都是用来对工程建模的,但它们还是有很大的区别,主要体现在AOV网是顶点表示活动的网,它只描述活动之间的制约关系,而AOE网是用边表示活动的网,边上的权值表示活动持续的时间,如图7-9-3所示两图的对比。因此,AOE网是要建立在活动之间制约关系没有矛盾的基础之上,再来分析完成整个工程需要多少时间,或者为缩短完成工程所需时间,应当加快哪些活动等问题。

我们把路径上各个活动所持续的时间之后称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上完成的活动叫关键活动。显然就图7-9-3的AOE网而言,开始->发动机完成->部件集中到位->组装完成就是关键路径,路径长度为5.5。

如果我们需要缩短整个工期,去改进轮子的生产效率,哪怕改动成0.1也无益于整个工期的变化,只有缩短关键路径上的关键活动时间才才可以减少整个工期长度。例如如果发动机制造缩短为2.5,整车组装缩短为1.5,那么关键路径就为4.5,整整缩短了一天的时间。

如果某项活动的最早开始时间和最晚开始时间一样,表示中间没有空隙,则此项活动就为关键活动。为此,我们需要定义以下几个参数。

1、事件的最早发生时间 etv(earliest time of vertex):即顶点vk 的最早发生时间。

2、事件的最晚发生时间 ltv(latest time of vertex):即顶点vk 的最短发生时间。也就是每个顶点对应的事件最晚需要开始的时间,超出此时间将会延误整个工期。

3、活动的最早开工时间 ete (earliest time of edge):即弧ak 的最早发生时间。

4、活动的最晚开工时间 lte (latest time of edge ):即弧ak 的最晚发生时间,也就是不推迟工期的最晚开工时间。

我们首先求得1和2,而 ete 本来是表示活动<vk, vj> 的最早开工时间,是针对弧来说的,但只有此弧的弧尾顶点vk的事件发生了,它才可以开始,因此ete = etv[k]。

而lte 表示的是活动<vk, vj> 的最晚开工时间,但此活动再晚也不能等vj 事件发生才开始,所以lte = ltv[j] - len<vk, vj> 。

最终,我们再来判断ete 和 lte 是否相等,相等意味着活动没有任何空闲,是关键路径,否则就不是。

现在来谈谈如何求etv 和 ltv。

假设我们现在已经求得顶点v0对应的 etv[0] =
0,顶点v1对应的etv[1] = 3, 顶点v2对应的etv[2] = 4, 现在我们需要求顶点v3对应的etv[3],其实就是求etv[1]
+ len<v1, v3> 与 etv[2] + len<v2, v3> 的较大值。显然 3+5 < 4+8,
得到etv[3] = 12, 如图7-9-5所示。

由此我们也可以得出计算顶点vk的最早发生时间即求etv[k]的公式是:

其中P[k] 表示所有到达顶点vk的弧的集合。比如图7-9-5的P[3]就是<v1, v3> 和 <v2, v3> 两条弧。len<vi, vk> 是弧<vi, vk>上的权值。

假如我们现在已经求得v9~ v5 顶点的ltv值,现在要求v4 的ltv 值,由邻接表可得到v4 有两条弧<v4, v6>, <v4, v7>,可以得到

ltv[4] = min(ltv[7] - 4, ltv[6] - 9) = 15,如图7-9-8所示。

可以发现,在计算ltv时,其实是把拓扑序列倒过来进行而已,因此可以得到计算顶点vk最晚发生时间即求ltv[k] 的公式是:

其中S[K]表示所有从顶点vk出发的弧的集合。比如图7-9-8的S[4] 就是<v4, v6>和<v4, v7>两条弧,len<vk, vj> 是弧<vk, vj> 上的权值。

现有一AOE网图如图7-9-4所示,我们使用邻接表存储结构,注意与拓扑排序时邻接表结构不同的地方在于,这里弧表结点增加了weight域,用来存储弧的权值。

求解事件的最早发生时间etv的过程,就是我们从头至尾找拓扑序列的过程,因此在求关键路径之前,需要先调用一次拓扑序列算法的代码来计算etv 和 拓扑序列列表,我们针对前面讲过的AOV网与拓扑排序的程序进行改进,代码如下(参考《大话数据结构》):

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 
/* 拓扑排序,若GL无回路,则输出拓扑排序序列并返回1,若有回路返回0。 */
bool TopologicalSort(GraphAdjList GL)
{
    EdgeNode *pe;
    int i, k, gettop;
    int top = 0;/* 用于栈指针下标  */
    int count = 0;/* 用于统计输出顶点的个数  */
    /* 建栈将入度为0的顶点入栈  */
    int *stack = (int *)malloc(GL->numVertexes * sizeof(int));

for (i = 0; i < GL->numVertexes; i++)
        if (0 == GL->adjList[i].in)
            stack[++top] = i;/* 将入度为0的顶点入栈 */

top2 = 0;
    etv = (int *)malloc(GL->numVertexes * sizeof(int));
    for (i = 0; i < GL->numVertexes; i++)
        etv[i] = 0; /* 初始化 */
    stack2 = (int *)malloc(GL->numVertexes * sizeof(int));

cout << "TopologicalSort ..." << endl;

while (top != 0)
    {
        gettop = stack[top--];
        cout << GL->adjList[gettop].data << " -> ";
        count++;  /* 输出i号顶点,并计数 */

stack2[++top2] = gettop; /* 将弹出的顶点序号压入拓扑序列的栈 */

for (pe = GL->adjList[gettop].firstedge; pe; pe = pe->next)
        {
            k = pe->adjvex;
            /* 将i号顶点的邻接点的入度减1,如果减1后为0,则入栈 */
            if (!(--GL->adjList[k].in))
                stack[++top] = k;
            /* 求各顶点事件的最早发生时间etv值 */
            if ((etv[gettop] + pe->weight) > etv[k])
                etv[k] = etv[gettop] + pe->weight;
        }
    }
    cout << endl;
    if (count < GL->numVertexes)
        return false;
    else
        return true;
}

在程序开始处我们声明了几个全局变量:

int *etv,*ltv; /* 事件最早发生时间和最迟发生时间数组,全局变量 */
int *stack2;   /* 用于存储拓扑序列的栈 */
int top2;   /* 用于stack2的指针 */

其中stack2用来存储拓扑序列,以便后面求关键路径时使用。

上面的拓扑排序函数中除了增加了第12~19行,29行,38~39行,其他跟前面讲过的AOV网与拓扑排序没什么区别。

第12~19行初始化全局变量etv数组、top2和stack2的过程。第29行就是将本来要输出的拓扑序列压入全局栈stack2中。第38~39行很关键,是求etv数组的每一个元素的值,具体求值办法参见AOE网和关键路径

下面来看求关键路径的算法代码。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
/* 求关键路径,GL为有向网,输出G的各项关键活动 */
void CriticalPath(GraphAdjList GL)
{
    EdgeNode *pe;
    int i, j, k, gettop;
    int ete, lte;/* 声明活动最早发生时间和最迟发生时间变量 */
    TopologicalSort(GL);/* 求拓扑序列,计算数组etv和stack2的值 */

ltv = (int *)malloc(GL->numVertexes * sizeof(int)); /* 事件最早发生时间数组 */

for (i = 0; i < GL->numVertexes; i++)
        ltv[i] = etv[GL->numVertexes - 1];/* 初始化 */

cout << "etv :  ";
    for (i = 0; i < GL->numVertexes; i++)
        cout << etv[i] << ' ';
    cout << endl;

while (top2 != 0)/* 出栈是求ltv */
    {
        gettop = stack2[top2--];
        /* 求各顶点事件的最迟发生时间ltv值 */
        for (pe = GL->adjList[gettop].firstedge; pe; pe = pe->next)
        {
            k = pe->adjvex;
            if (ltv[k] - pe->weight < ltv[gettop])
                ltv[gettop] = ltv[k] - pe->weight;
        }
    }

cout << "ltv :  ";
    for (i = 0; i < GL->numVertexes; i++)
        cout << ltv[i] << ' ';
    cout << endl;
    /* 求ete,lte和关键活动 */
    for (j = 0; j < GL->numVertexes; j++)
    {
        for (pe = GL->adjList[j].firstedge; pe; pe = pe->next)
        {
            k = pe->adjvex;
            ete = etv[j];/* 活动最早发生时间 */
            lte = ltv[k] - pe->weight;/* 活动最迟发生时间 */
            if (ete == lte) /* 两者相等即在关键路径上 */
                cout << "<v" << GL->adjList[j].data << " - v"
                     << GL->adjList[k].data << "> length: " << pe->weight << endl;
        }
    }
}

函数第7行调用求拓扑序列的函数,执行完毕后,全局数组etv和栈stack2 如图7-9-6所示,top2 = 10,也就是说,对于每个事件的最早发生时间,我们已经计算出来了。

第11~12行初始化全局变量ltv数组,因为etv[9] = 27,所以数组ltv值现在为全27。

第19~29行是计算ltv 数组的循环,具体方法参见AOE网和关键路径

当程序执行到第36行,etv和ltv数组的值如图7-9-9

比如etv[1] = 3, 而ltv[1] = 7,表示如果单位是天的话,哪怕v1整个事件在第7天才开始,也可以保证整个工程的按期完成,可以提前v1事件开始时间,但最早也得第3天开始。

第36~47行是求另两个变量,活动最早开始时间ete和活动最晚开始时间lte,并对相同下标的它们进行比较。两重循环嵌套是对邻接表的顶点和每个顶点的弧表遍历,具体方法参见AOE网和关键路径,举例来说,如图7-9-10,当j = 0时,当k = 2, ete = lte,
表示

弧<v0, v2> 是关键路径,因此打印;当k = 1, ete != lte, 故弧<v0, v1> 不是关键路径。

j = 1 一直到 j = 9为止,做法是完全相同的,最后输出的结果如下图,最终关键路径如图7-9-11所示。

AOE网与关键路径简介的更多相关文章

  1. 基于AOE网的关键路径的求解

    [1]关键路径 在我的经验意识深处,“关键”二字一般都是指临界点. 凡事万物都遵循一个度的问题,那么存在度就会自然有临界点. 关键路径也正是研究这个临界点的问题. 在学习关键路径前,先了解一个AOV网 ...

  2. _DataStructure_C_Impl:AOE网的关键路径

    //_DataStructure_C_Impl:CriticalPath #include<stdio.h> #include<stdlib.h> #include<st ...

  3. AOE网与关键路径

    声明:图片及内容基于https://www.bilibili.com/video/BV1BZ4y1T7Yx?from=articleDetail 原理 AOE网 关键路径 数据结构 核心代码 Topo ...

  4. AOE网的关键路径的计算

    求关键路径,只需理解顶点(事件)和边(活动)各自的两个特征属性以及求法即可: Ø  先根据首结点的Ve(j)=0由前向后(正拓扑序列)计算各顶点的最早发生时间 Ø  再根据终结点的Vl(j)等于它的V ...

  5. 教你轻松计算AOE网关键路径(转)

    原文链接:http://blog.csdn.net/wang379275614/article/details/13990163 本次结合系统分析师-运筹方法-网络规划技术-关键路径章节,对原文链接描 ...

  6. 教你轻松计算AOE网关键路径

    认识AOE网 有向图中,用顶点表示活动,用有向边表示活动之间开始的先后顺序,则称这种有向图为AOV网络:AOV网络可以反应任务完成的先后顺序(拓扑排序). 在AOV网的边上加上权值表示完成该活动所需的 ...

  7. SDUT 2498 AOE网上的关键路径

    AOE网上的关键路径 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 一个无环的有向图称为无 ...

  8. 数据结构关于AOV与AOE网的区别

    AOV网,顶点表示活动,弧表示活动间的优先关系的有向图. 即如果a->b,那么a是b的先决条件. AOE网,边表示活动,是一个带权的有向无环图, 其中顶点表示事件,弧表示活动,权表示活动持续时间 ...

  9. AOV图与拓扑排序&AOE图与关键路径

    AOV网:所有的工程或者某种流程可以分为若干个小的工程或阶段,这些小的工程或阶段就称为活动.若以图中的顶点来表示活动,有向边表示活动之间的优先关系,则这样活动在顶点上的有向图称为AOV网. 拓扑排序算 ...

随机推荐

  1. WordPress后台的文章、分类,媒体,页面,评论,链接等所有信息中显示ID并将ID设置为第一列

    WordPress后台默认是不显示文章.分类等信息ID的,查看起来非常不方便,不知道Wp团队出于什么原因默认不显示这个但可以使用Simply Show IDs插件来实现 不使用插件,其他网友的实现: ...

  2. 转: telnet命令学习

    1.每天一个linux命令(58):telnet命令 转自: http://www.cnblogs.com/peida/archive/2013/03/13/2956992.html telnet命令 ...

  3. 微信小程序中用户唯一ID的获取

    折腾到半夜,搞得挺兴奋,总结一下,免得忘了: 1.微信小程序直接获得的是一些简单信息,基本无用 2.用户唯一标识是openid,还有一个unionid是关联多个公众号之类情况下用,我不大关心 3.在g ...

  4. (剑指Offer)面试题60:把二叉树打印成多行

    题目: 从上到下按层打印二叉树,同一层结点从左至右输出.每一层输出一行. 思路: 很明显,采用广度优先遍历来解决,但因为需要按行输出,所以需要判断每一层的开始和结束,因此需要两个变量,一个表示当前层尚 ...

  5. asp.net 使用JS获得串口数据

    使用JS获得串口数据 JavaScript语言通常是一种网页编程语言,比较适合前台的一些应用设计.对于本地设备和数据的操作有比较大的限制.由于项目的需要,我需要获得本机的串口数据并显示在web端.我们 ...

  6. 关于ARM立即数的理解

    转自:http://blog.sina.com.cn/s/blog_9bad4f090101e14d.html 一.立即数指令码的组成 ARM公司将32bit指令码分为如下两部分: 32bit立即数通 ...

  7. 安装 Flex2packagebeta_1.994

    下载文件 解压到/var/root/Media/Cydia/AutoInstall/这个路径重启手机,Cydia会自动安装好DEB文件的 patch路径 下载 /private/var/mobile/ ...

  8. vbScript 备忘

    closeCDRom.vbs Set Obj=CreateObject("WMPlayer.OCX.7") Set CD=Obj.CDROMCollection then CD.I ...

  9. poj 4014 Dice 贪心

    //poj 4014 //sep9 #include <iostream> #include <algorithm> using namespace std; int n; s ...

  10. Best Time to Buy and Sell Stock I &amp;&amp; II &amp;&amp; III

    题目1:Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of ...