关键路径

在我的经验意识深处,“关键”二字一般都是指临界点。

凡事万物都遵循一个度的问题,那么存在度就会自然有临界点。

关键路径也正是研究这个临界点的问题。

在学习关键路径前,先了解一个AOV网和AOE网的概念:

用顶点表示活动,用弧表示活动间的优先关系的有向图:

称为顶点表示活动的网(Activity On Vertex Network),简称为AOV网。

与AOV网对应的是AOE(Activity On Edge)网即边表示活动的网。

AOE网是一个带权的有向无环图。

网中只有一个入度为零的点(称为源点)和一个出度为零的点(称为汇点)。

其中,顶点表示事件(Event),弧表示活动,权表示活动持续的时间。

通常,AOE网可用来估算工程的完成时间。

假如汽车生产工厂要制造一辆汽车,制造过程的大概事件和活动时间如上图AOE网:

我们把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。

那么,显然对上图AOE网而言,所谓关键路径:

开始-->发动机完成-->部件集中到位-->组装完成。路径长度为5.5。

如果我们试图缩短整个工期,去改进轮子的生产效率,哪怕改动0.1也是无益的。

只有缩短关键路径上的关键活动时间才可以减少整个工期的长度。

例如如果制造发动机缩短为2.5天,整车组装缩短为1.5天,那么关键路径为4.5。

工期也就整整缩短了一天时间。

好吧! 那么研究这个关键路径意义何在?

假定上图AOE网中弧的权值单位为小时,而且我们已经知道黑深色的那一条为关键路径。

假定现在上午一点,对于外壳完成事件而言,为了不影响工期:

外壳完成活动最早也就是一点开始动工,最晚在两点必须要开始动工。

最大权值3表示所有活动必须在三小时之后完成,而外壳完成只需要2个小时。

所以,这个中间的空闲时间有一个小时,为了不影响整个工期,它必须最迟两点动工。

那么才可以保证3点时与发动机完成活动同时竣工,为后续的活动做好准备。

对AOE网有待研究的问题是:

(1)完成整个工程至少需要多少时间?

(2)那些活动是影响工程进度的关键?

今天研究是实例如下图所示:

假想是一个有11项活动的AOE网,其中有9个事件(V1,V2,V3...V9)。

每个事件表示在它之前的活动已经完成,在它之后的活动可以开始。

如V1表示整个工程开始,V9表示整个共结束,V5表示a4和a5已经完成,a7和a8可以开始。

【2】关键路径算法

为了更好的理解算法,我们先需要定义如下几个参数:

(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的最晚发生时间,也就是不推迟工期的最晚开工时间。

然后根据最早开工时间ete[k]和最晚开工时间lte[k]相等判断ak是否是关键路径。

将AOE网转化为邻接表结构如下图所示:


与拓扑序列邻接表结构不同的地方在于,弧链表增加了weight域,用来存储弧的权值。

求事件的最早发生时间etv的过程,就是从头至尾找拓扑序列的过程。

因此,在求关键路径之前,先要调用一次拓扑序列算法的代码来计算etv和拓扑序列表。

数组etv存储事件最早发生时间

数组ltv存储事件最迟发生时间

全局栈用来保存拓扑序列

注意代码中的粗部分与原拓扑序列的算法区别。

第11-15行 初始化全局变量etv数组。

第21行 就是讲要输出的拓扑序列压入全局栈。

第 27-28 行很关键,它是求etv数组的每一个元素的值。

比如:假如我们已经求得顶点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,在代码中e->weight就是当前弧的长度。如图所示:

由此也可以得到计算顶点Vk即求etv[k]的最早发生时间公式如上。

下面具体分析关键路径算法:

1.  程序开始执行。第5行,声明了etv和lte两个活动最早最晚发生时间变量

2.  第6行,调用求拓扑序列的函数。

  执行完毕后,全局数组etv和栈的值如下所示796,也就是说已经确定每个事件的最早发生时间。

3.  第7-9行初始化数组ltv,因为etv[9]=27,所以数组当前每项均为27。

4.  第10-19行为计算ltv的循环。第12行,先将全局栈的栈头出栈,由后进先出得到gettop=9。

  但是,根据邻接表中信息,V9没有弧。所以至此退出循环。

5.  再次来到第12行,gettop=8,在第13-18行的循环中,V8的弧表只有一条<V8,V9>

  第15行得到k=9,因为ltv[9]-3<ltv[8],所以ltv[8]=ltv[9]-3=24,过程如下图所示:

6.  再次循环,当gettop=7,5,6时,同理可计算出ltv相对应的值为19,25,13。

  此时ltv值为:{27,27,27,27,27,13,25,19,24,27}

7.  当gettop=4时,由邻接表信息可得到V4有两条弧<V4,V6>和<V4,V7>。

  通过第13-18行的循环,可以得到ltv[4]=min(ltv[7]-4,ltv[6]-9)=min(19-4,25-9)=15

  过程分析如下图所示:

  当程序执行到第20行时,相关变量的值如下图所示。

  比如etv[1]=3而ltv[1]=7表示(如果单位按天计的话):

  哪怕V1这个事件在第7天才开始也是可以保证整个工程按期完成。

  你也可以提前V1时间开始,但是最早也只能在第3天开始。

8.  第20-31行是求另两个变量活动最早开始时间ete和活动最晚时间lte。

  当 j=0 时,从V0顶点开始,有<V0,V2>和<V0,V1>两条弧。

  当 k=2 时,ete=etv[j]=etv[0]=0

  lte=ltv[k]-e->weight=ltv[2]-len<v0,v2>=4-4=0  此时ete == lte

  表示弧<v0,v2>是关键活动,因此打印。

  当 k=1 时,ete=etv[j]=etv[0]=0

  lte=ltv[k]-e->weight=ltv[2]-len<v0,v1>=7-3=4  此时ete != lte

  表示弧<v0,v1>并不是关键活动。如图所示:

说明:ete表示活动<Vk,Vj>的最早开工时间,是针对弧来说的。

但是只有此弧的弧尾顶点Vk的事件发生了,它才可以开始,ete=etv[k]。

lte表示的是活动<Vk,Vj>最晚开工时间,但此活动再晚也不能等V1事件发生才开始。

而必须要在V1事件之前发生,所以lte=ltv[j]-len<Vk,Vj>。

9.  j=1 直到 j=9 为止,做法完全相同。

最终关键路径如下图所示:

注意:本例是唯一一条关键路径,并不等于不存在多条关键路径。

如果是多条关键路径,则单是提高一条关键路径上的关键活动速度并不是能导致整个工程缩短工期、

而必须提高同时在几条关键路径上的活动的速度。

【3】关键路径是代码实现

本示例代码与算法有些不同,但是效果相同,都是为了达到一个共同目的:理解并学习关键路径算法。

#include <iostream>
#include "Stack.h"
#include <malloc.h>
using namespace std;#define  MAXVEX   10
#define  MAXEDGE  13// 全局栈
SeqStack<int> sQ2;typedef struct EdgeNode
{int adjvex;    // 邻接点域,存储该顶点对应的下标int weight; // 边的权值struct EdgeNode* next; // 链域
} EdgeNode;typedef struct VertexNode
{int inNum;    // 顶点入度值int data;    // 顶点数值欲EdgeNode* firstedge; // 边表头指针
} VertexNode, AdjList[MAXVEX];typedef struct
{AdjList adjList;int numVertexes, numEdges; // 图中当前顶点数和边数(对于本案例,已经存在宏定义)
} graphAdjList, *GraphAdjList;// 构建节点
EdgeNode* BuyNode()
{EdgeNode* p = (EdgeNode*)malloc(sizeof(EdgeNode));p->adjvex = -1;p->next = NULL;return p;
}
// 初始化图
void InitGraph(graphAdjList& g)
{for (int i = 0; i < MAXVEX; ++i){g.adjList[i].firstedge = NULL;}
}
// 创建图
void CreateGraph(graphAdjList& g)
{int i = 0, begin = 0, end = 0, weight = 0;EdgeNode *pNode = NULL;cout << "输入10个顶点信息(顶点 入度):" << endl;for (i = 0; i < MAXVEX; ++i){cin >> g.adjList[i].data >> g.adjList[i].inNum;}cout << "输入13条弧的信息(起点 终点 权值):" << endl;for (i = 0; i < MAXEDGE; ++i){cin >> begin >> end >> weight;pNode = BuyNode();pNode->adjvex = end;pNode->weight = weight;pNode->next = g.adjList[begin].firstedge;g.adjList[begin].firstedge = pNode;}
}
// 打印输入信息的逻辑图
void PrintGraph(graphAdjList &g)
{cout << "打印AOE网的邻接表逻辑图:" << endl;for (int i = 0; i < MAXVEX; ++i){cout << " " << g.adjList[i].inNum << " " << g.adjList[i].data << " ";EdgeNode* p = g.adjList[i].firstedge;cout << "-->";while (p != NULL){int index = p->adjvex;cout << "[" << g.adjList[index].data << " " << p->weight << "] " ;p = p->next;}cout << endl;}
}
// 求拓扑序列
bool TopologicalSort(graphAdjList g, int* pEtv)
{EdgeNode* pNode = NULL;int i = 0, k = 0, gettop = 0;int nCnt = 0;SeqStack<int> sQ1;for (i = 0; i < MAXVEX; ++i){if (0 == g.adjList[i].inNum)sQ1.Push(i);}for (i = 0; i < MAXVEX; ++i){pEtv[i] = 0;}while (!sQ1.IsEmpty()){sQ1.Pop(gettop);++nCnt;sQ2.Push(gettop); // 将弹出的顶点序号压入拓扑序列的栈if (MAXVEX == nCnt){    //去掉拓扑路径后面的-->cout << g.adjList[gettop].data << endl; break;}cout << g.adjList[gettop].data << "-->";pNode = g.adjList[gettop].firstedge;while (pNode != NULL){k = pNode->adjvex;--g.adjList[k].inNum;if (0 == g.adjList[k].inNum)sQ1.Push(k);if (pEtv[gettop] + pNode->weight > pEtv[k])pEtv[k] = pEtv[gettop] + pNode->weight;pNode = pNode->next;}}return nCnt != MAXVEX;
}
// 关键路径
void CriticalPath(graphAdjList g, int* pEtv, int* pLtv)
{// pEtv  事件最早发生时间// PLtv  事件最迟发生时间EdgeNode* pNode = NULL;int i = 0, gettop = 0, k =0, j = 0;int ete = 0, lte = 0; // 声明活动最早发生时间和最迟发生时间变量for (i = 0; i < MAXVEX; ++i){pLtv[i] = pEtv[MAXVEX-1]; // 初始化}while (!sQ2.IsEmpty()){sQ2.Pop(gettop); // 将拓扑序列出栈,后进先出pNode = g.adjList[gettop].firstedge;while (pNode != NULL){    // 求各顶点事件的最迟发生时间pLtv值k = pNode->adjvex;if (pLtv[k] - pNode->weight < pLtv[gettop])pLtv[gettop] = pLtv[k] - pNode->weight;pNode = pNode->next;}}// 求 ete, lte, 和 关键路径for (j = 0; j < MAXVEX; ++j){pNode = g.adjList[j].firstedge;while (pNode != NULL){k = pNode->adjvex;ete = pEtv[j]; // 活动最早发生时间lte = pLtv[k] - pNode->weight; // 活动最迟发生时间if (ete == lte)cout << "<V" << g.adjList[j].data << ",V" << g.adjList[k].data << "> :" << pNode->weight << endl;pNode = pNode->next;}}
}
void main()
{graphAdjList myg;InitGraph(myg);cout << "创建图:" << endl;CreateGraph(myg);cout << "打印图的邻接表逻辑结构:" << endl;PrintGraph(myg);int* pEtv = new int[MAXVEX];int* pLtv = new int[MAXVEX];cout << "求拓扑序列(全局栈sQ2的值):" << endl;TopologicalSort(myg, pEtv);cout << "打印数组pEtv(各个事件的最早发生时间):" << endl;for(int i = 0; i < MAXVEX; ++i){cout << pEtv[i] << " ";}cout << endl << "关键路径:" << endl;CriticalPath(myg, pEtv, pLtv);cout << endl;
}
/*
创建图:
输入10个顶点信息(顶点 入度):
0 0
1 1
2 1
3 2
4 2
5 1
6 1
7 2
8 1
9 2
输入13条弧的信息(起点 终点 权值):
0 1 3
0 2 4
1 3 5
1 4 6
2 3 8
2 5 7
3 4 3
4 6 9
4 7 4
5 7 6
6 9 2
7 8 5
8 9 3
打印图的邻接表逻辑结构:
打印AOE网的邻接表逻辑图:
0 0 -->[2 4] [1 3]
1 1 -->[4 6] [3 5]
1 2 -->[5 7] [3 8]
2 3 -->[4 3]
2 4 -->[7 4] [6 9]
1 5 -->[7 6]
1 6 -->[9 2]
2 7 -->[8 5]
1 8 -->[9 3]
2 9 -->
求拓扑序列(全局栈sQ2的值):
0-->1-->2-->3-->4-->6-->5-->7-->8-->9
打印数组pEtv(各个事件的最早发生时间):
0 3 4 12 15 11 24 19 24 27
关键路径:
<V0,V2> :4
<V2,V3> :8
<V3,V4> :3
<V4,V7> :4
<V7,V8> :5
<V8,V9> :3*/

AOE网:关键路径和关键活动相关推荐

  1. [图] AOE网-关键路径|关键活动-原理、手算举例、C语言实现

    文章目录 AOE网 AOE的应用(AOE的相关概念) 原理:求关键活动和关键路径 求ve.vl(顶点) 求ee.el(边) 求关键路径,关键活动 手算举例 C语言实现 AOE网 [有向无环图]活动在边 ...

  2. 数据结构 — 图 之 关键路径、关键活动 (文字表述)

    1.AOE网: 边表示活动的网络,边表示活动,顶点表示事件,当该事件发生了,就说明触发该事件发生的所有的活动已经完成.一个工程所需的最短时间 就是 完成从开始顶点到终止顶点的最长路径的长度. 2.关键 ...

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

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

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

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

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

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

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

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

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

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

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

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

  9. 图的应用(AOV网、AOE网、图连通性)

    图的应用(AOV网.AOE网.图连通性): AOV网: 在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,称这样的有向图为顶点表示活动的网,简称AOV网. AOV网特点: 1.AO ...

最新文章

  1. 在 Delphi 下使用 DirectSound (5): 获取或设置缓冲区的格式:
  2. 表达式树amp;amp;无根树转化为有根树
  3. 算法提高课-图论-有向图的强连通分量-AcWing 367. 学校网络:强连通分量、tarjan算法
  4. android 6.0 log,android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中
  5. CF79D Password(P3943 星空)
  6. ppt生成器_小米发布会ppt词云怎么做的
  7. NodeBlog v0.1.0发布
  8. 在OpenEIM中每运行一个程序
  9. docker container
  10. html 头标签 meta http-equiv 属性应用。
  11. ZJOI2018 Round2 被踩记被虐记
  12. WannaCry 勒索软件
  13. php单选框关联数据库字段_php select,radio和checkbox默认选择的实现方法
  14. Qt引入图标字体包iconfont
  15. 《点燃我,温暖你》爱心代码复现
  16. 《传奇之王》风波不断 编剧替柳云龙打抱不平_0
  17. word 插入mathtype公式对象后,默认环绕方式是浮于文字上方,每次插入后都得手动改成嵌入,如何解决?
  18. 平头哥面试——数字IC1
  19. 细说马斯洛需求层次理论与产品的关系——老吴说产品
  20. onHoverEvent

热门文章

  1. linux 下MySQL本地安装mysql - u root - p 无法登入
  2. android自定义桌面(launcher)
  3. 电脑上传,提高电脑上传速度 怎么提高文件上传速度
  4. 基于涂鸦智能开发的墨水屏座位管理器——2.嵌入式功能实现篇
  5. 【Spring+Mybatis】 Invalid bound statement (not found): com.xxxx.mapper.UserMapper.selectUser
  6. 测试同学反馈,java 程序内存泄露,症状是RSS不断增加超过了jvm的xmx
  7. 开发一款APP需要阿里云服务器多大配置
  8. BG2RHE - 树莓派3B+外置wifi解决金属外壳屏蔽问题
  9. python验证身份证最后一位数字代表什么_身份证最后一位不是数字而是X,代表什么含义?涨知识了...
  10. border设置1px看起来很粗问题