AOE网与关键路径简介

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 集合体系详解——List体系有序集合

引言 面向对象语言对事物的体现必然是以对象的形式&#xff0c;Java工程师为了方便多多个对象的操作&#xff0c;就对对象进行存储&#xff0c;集合就是存储对象的一种方式&#xff0c;他们的底层都是基于不同的数据结构。当然集合和数组一样都是容器&#xff0c;数组也是可以存…

alert闪一下就没了_尾部贯穿式镀铬银饰条除了丑,还能闪瞎眼

尾部贯穿式镀铬银饰条&#xff0c;在2010年代成为诸多汽车品牌车型争相采用的新世纪新标配&#xff0c;配以双边排气&#xff0c;让整个车尾看起来层次感强烈&#xff0c;视觉收窄&#xff0c;几十万的奥迪A8L有&#xff0c;十几万的斯柯达速派有&#xff0c;A级车有&#xff0…

docker 指定网卡_Docker | Docker技术基础梳理(五) Docker网络管理

为什么需要容器的网络管理&#xff1f;容器的网络默认与宿主机、与其他容器相互隔离&#xff0c;且容器中可以运行一些网络应用&#xff0c;比如nginx、web应用、数据库等&#xff0c;如果需要让外部也可以访问这些容器中运行的网络应用&#xff0c;那么就需要配置网络来实现。…

Android安装两次才成功,Android应用从市场安装完成打开与桌面打开,被启动两次的问题...

问题描述&#xff1a;1、从Android应用市场下载并安装应用&#xff0c;安装完成后&#xff0c;当前界面下方会出现“打开”按钮&#xff0c;这时候我们点击“打开”&#xff0c;会启动应用&#xff0c;进入到应用的启动页面&#xff0c;然后进入应用的主界面&#xff0c;这个时…

事务保存点

在SQL Server中使用rollback会回滚所有的未提交事务状态&#xff0c;但是有些时候我们只需要回滚部分语句&#xff0c;把不需要回滚的语句提到事务外面来&#xff0c;虽然是个方法&#xff0c;但是却破坏了事务的ACID。 SQL中使用事务保存点 即可解决这个问题. 一.SQL 事务中存…

鼎信诺审计前端取数工具_给2019前端的5个建议

2019 农历新年即将到来&#xff0c;是时候总结一下团队过去一年的技术沉淀。过去一年我们支撑的数据相关业务突飞猛进&#xff0c;其中两个核心平台级产品代码量分别达到30万行和80万行&#xff0c;TS 模块数均超过1000个&#xff0c;协同开发人员增加到20人。由于历史原因&…

Hadoop HA

HA概念&#xff1a; high avalability 高可用性。 hadoop 1.x非ha设计 Secondnode对元数据的可靠性有了保障&#xff0c;但服务的可用性不高。 即&#xff1a;当Namenode节点宕机了&#xff0c;整个hadoop就不能使用了&#xff0c;影响了client的使用。 hadoop 2.x的ha设计 新…

紫光展锐处理器有那些手机用_酷派将发千元5G手机,国产紫光展锐加持,主打性价比...

↑↑↑点击上方蓝字订阅每日最新热点手机资讯数年之前&#xff0c;“中华酷联”是国产智能手机的四大代表。不过随着越来越多的强力竞争者入局&#xff0c;中兴、酷派、联想渐渐衰败&#xff0c;仅剩华为屹立手机行业顶端。但是酷派似乎从未想过放弃&#xff0c;最近便要发布一…

jelly bean android,Jelly Bean占Android系统份额突破10%

Android系统份额图(腾讯科技配图)腾讯科技讯(清雨)北京时间1月4日消息&#xff0c;据国外媒体报道&#xff0c;微博)周四发布最新数据显示&#xff0c;Android 4.1版本和Android 4.2版本的Jelly Bean在Android系统中的份额超过了10%&#xff0c;另外Android 4.0版本的ICS的份额…

妲己机器人需要什么条件才能使用_estar零封YTG:平头哥快乐电竞,只有妲己没亚瑟,差评...

2020KPL秋季赛常规赛第8周最后1个比赛日的第2场比赛&#xff0c;结果已经尘埃落定了。而最终的比赛结果是&#xff1a;estarpro轻松以3比0的大比分零封战胜YTG。有一说一&#xff0c;这一场比赛真的是毫无悬念啊&#xff0c;甚至双方交手的第1小局比赛&#xff0c;estarpro只用…

python离线录音转文字_Python将文字转成语音并读出来的实例详解

前言 本篇文章主要介绍&#xff0c;如何利用Python来实现将文字转成语音。将文字转成语音主要有两种不同的实现方法&#xff1a;先将文字转成语音&#xff0c;然后再通过读取语音实现发音、直接调用系统内置的语音引擎实现发音&#xff0c;后一种方法的实现主要利用第三方库。 …

魅族15系统是android,魅族15系列评测:性能够用王者荣耀优化

硬件性能&#xff1a;中配够用&#xff0c;高配优秀硬件方面&#xff0c;文章前面的参数表已经写得很清楚&#xff0c;魅族15搭载的是高通骁龙660处理器&#xff0c;并配备4GB的运行内存&#xff1b;魅族15 Plus则搭载三星Exynos 8895&#xff0c;配备6GB运行内存。在目前的移动…

.net 怎么循环得到数组里的值_HashMap 底层实现、加载因子、容量值及死循环

写在前面&#xff1a;2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上&#xff0c;内容详细&#xff0c;图文并茂&#xff0c;有需要学习的朋友可以Star一下&#xff01;GitHub地址&#xff1a;abel-max/Java-Study-NoteHashMap 简介HashMap 是一个基于哈希表…

hdfs命令

bin/hdfs dfs命令 appendToFile Usage: hdfs dfs -appendToFile <localsrc> ... <dst> 追加一个或者多个文件&#xff08;linux文件&#xff09; <localsrc> ...到hdfs制定文件<dst>中.也可以从命令行读取输入. hdfs dfs -appendToFile localfile /use…

eclipse jdk配置_eclipse的安装和jdk的配置(JAVA)

首先需要到eclipse官网下载(eclipse.org)点击download进入新界面点击download 64bit进入新界面 点击划线的&#xff0c;点击download也许但是比较慢&#xff0c;点击划线的会出现扩展选项&#xff0c;选择距离你比较近的节点(速度比较快)作者选的是C…

webview跟html通信的原理,1.iOS: webView与html的交互

摘要:由于最近的项目中大部分功能需要 iOS 原生端与 html 进行交互才能完美实现,所以对 iOS 与 html 的交互方式进行了学习,这篇文章主要介绍 WebViewJavascriptBridge 框架的使用.至于原生的 JavaScriptCore.framework 就不多介绍了,同时在这里推荐一个比较好的博客.http://bl…

HDFS Federation(HDFS 联盟)介绍

1. 当前HDFS架构和功能概述 我们先回顾一下HDFS功能。HDFS实际上具有两个功能&#xff1a;命名空间管理&#xff08;Namespace management&#xff09;和块/存储管理服务&#xff08;block/storage management&#xff09;。 1.1 命名空间管理 HDFS的命名空间包含目录、文件和块…

linux java 部署 生产环境

2019独角兽企业重金招聘Python工程师标准>>> 下载文件 首先进入网页&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 点击Accept License Agreement后选择jdk-8u161-linux-x64.tar.gz&#xff0c;下载。 配置环…

华为鸿蒙发布作文,华为鸿蒙OS定档6月2日发布!MatePad Pro 2或同台亮相:首发预装...

5月25日一早&#xff0c;原华为EMUI官微就正式宣布更名为Harmony OS&#xff0c;并宣布将在6月2日晚20点召开鸿蒙操作系统及华为全场景新品发布会&#xff0c;届时将正式发布鸿蒙OS正式版。据近期进行开发者测试的用户反馈&#xff0c;鸿蒙OS目前已经非常完善&#xff0c;且稳定…

python如何根据数据画散点图_如何用python画出样本的散点图?

用python画样本散点图的方法&#xff1a; 数据&#xff08;取第一列作为x&#xff0c;取第四列作为y&#xff09;如下&#xff1a;实现代码如下&#xff1a;import matplotlib.pyplot as plt import numpy as np # 定义画散点图的函数 def draw_scatter(n, s): ""&qu…