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

在前面讲了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所示。

转载于:https://www.cnblogs.com/alantu2018/p/8471833.html

AOE网与关键路径简介相关推荐

  1. AOE网与关键路径、关键路径算法

    AOE网与关键路径 在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动持续的时间,称这样的有向图为边表示活动的网,简称 AOE网(activity on edge ne ...

  2. dhu 6 获取AOE网的关键路径

    6 获取AOE网的关键路径 作者: 冯向阳时间限制: 1S章节: 课程设计 问题描述 : 建立一个有向网AOE网,设计并完成一算法Get_CriticalPath(),获取关键路径.该路径仅输出,不须 ...

  3. 图论 —— AOE 网与关键路径

    [AOE 网] 在表示一个工程时,用顶点表示事件,用弧表示活动,权值表示活动的持续时间,这样的有向图即为 AOE 网. 其有两个性质: 在顶点表示事件发生之后,从该顶点出发的有向弧所表示的活动才能开始 ...

  4. 【数据结构和算法笔记】AOE网和关键路径

    目录 AOE网的概念: 关键路径:(critical  path) 求关键路径和关键活动: 事件的最早开始时间(event early): 事件的最迟开始时间(event late): 活动的最早开始 ...

  5. AOE网:关键路径和关键活动

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

  6. 选择题快速求解AOE网的关键路径

    #引言 求解AOE网关键路径时,书上的方法为先从源点到汇点求解事件最早发生时间ve,再从汇点到源点求解事件最迟发生时间vl,再利用ve和vl求解每个活动的最早开始时间e(i)和最迟开始时间l(i),e ...

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

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

  8. 有向无环图——AOE网(关键路径)

    有向无环图:无环的有向图,简称DAG图(Directed Acycline Graph) 有向无环图常用来描述一个工程或系统的进行过程.(通常吧计划.施工.生产.程序流程等当成是一个工程) 一个工程可 ...

  9. 数据结构:图:AOV网和AOE网

    AOV网(顶点表示活动的网):以有向图中的顶点来表示活动,以有向边来表示活动之间的先后次序关系. AOV网的拓扑序列:AOV网的拓扑序列就是将AOV网中的所有顶点排成一个线性序列:拓扑序列实际上就是要 ...

最新文章

  1. java培训分享:java软件开发可以用哪些软件?
  2. oracle 查看表结构约束,oracle 约束
  3. NeurIPS 2020 | 自步对比学习:充分挖掘无监督学习样本
  4. DEDE 会员调用方法详解
  5. golang 复制对象的正确做法
  6. 如何在不跳转的情况下实现用户登录
  7. C++语言基础 —— STL —— 容器与迭代器 —— set 与 multiset
  8. TortoiseSVN 不显示图标
  9. php 请求远程链接
  10. CentOS7安装VPP(FD.io)
  11. springboot细节挖掘(知识积累)
  12. 【Python笔记】AttributeError: module 'urllib3' has no attribute 'PoolManager'
  13. 百度在线编辑器 代码高亮
  14. 苹果Mac重复文件清理工具:​​​​Tidy Up
  15. Java初学练手,一款汽车车牌号生成小工具
  16. mysql命令执行cmd命令_mysql cmd常用命令
  17. Android 实现百度地图骑行路线规划,骑行路线规划
  18. C语言BMP图像的读取、存入、水平镜像、竖直镜像、马赛克模糊处理、灰度二值化处理
  19. 吃完饭后,到底是躺着、坐着、站着还是运动?看完终于不纠结了
  20. 力扣 两数相加 C语言 题解

热门文章

  1. android unix时间,android: 日期转Unix时间戳,Unix时间戳转日期,带时区
  2. cx oracle 中文 u,cx\U Oracle永久连接
  3. 连接驱动_在jdbc中完成对于jdbc参数、jdbc变量,加载驱动,创建连接的封装
  4. php mysql电商网站设计与制作_赢在电子商务——php+mysql电商网站设计与制作
  5. php历史上的今天源码,代码获取历史上的今天发生的事_基础知识
  6. Oracle中获取文件中的数据,操作oracle中的数据文件
  7. 什么意思是谁_舔狗是什么意思?如果不是真的喜欢谁又愿意做舔狗呢出处?
  8. 双端堆c语言,数据结构——双端堆(C语言)
  9. Maven 系列 1:Maven 安装与环境变量的配置完整步骤及需要注意的问题(以 jdk1.8、Maven 3.6.2 和 win10 为例,附下载地址)
  10. 共同体不是c语言中的一个数据类型,《c语言程序设计教学资料》第12章---构体和共同体.ppt...