目录
一、什么是AOE网?
1.1 AOE网的定义和性质
AOE网:在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间,称这样的有向图为边表示活动的网(activity on edge network),简称AOE网。
在AOE网中存在唯一的、入度为0的顶点,称为源点;存在唯一的、出度为0的顶点,称为汇点。
AOE网的性质:
- 只有某顶点所代表的的事件发生后,从该顶点出发的各活动才能开始;
- 只有在进入某顶点的各活动都结束后,该顶点所代表的事件才能发生。
AOE网示例:
1.2 AOE网的应用
AOE网可以回答下列问题:
- 完成整个工程至少需要多少时间?
- 为缩短完成工程所需的时间,应当加快哪些活动?
至少需要多少时间才能使事件V4发生?
由图可知,必须在完成活动a1、a2、a4、a5后才能使事件V4发生。完成a1和a4总共需要的时间为7,完成a2和a5总共需要的时间为5。因此,至少需要7时间才能使事件V4发生。
得出结论:
- 最短工期应该是最长路径;
- 加快最长路径上的活动就能缩短最短工期。
二、什么是关键路径?
2.1 关键路径和关键活动的定义
关键路径:在AOE网中,从源点到汇点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。
关键活动:关键路径上的活动称为关键路径。
关键路径可能不只一条,重要的是找到关键活动。
2.2 寻找关键活动
要找出关键路径,必须找出关键活动,即不按期完成就会影响整个工程进度的活动。
首先计算以下与关键活动有关的量:
- 事件的最早发生时间 ve[i];
- 事件的最晚发生时间 vl[i];
- 活动的最早开始时间 ee[i];
- 活动的最晚开始时间 el[i]。
最后计算各个活动的时间余量 el[i] - ee[i],时间余量为0者即为关键活动。
2.2.1 事件的最早发生时间ve[i]
计算下图所有事件的最早发生时间:
2.2.2 事件的最晚发生时间vl[i]
计算下图所有事件的最晚发生时间:
2.2.3 活动的最早开始时间ee[i]
计算下图所有活动的最早开始时间:
2.2.4 活动的最晚开始时间el[i]
计算下图所有活动的最晚开始时间:
三、关键路径算法实现
3.1 修改拓扑排序算法
修改后的拓扑排序算法便于同时求出每个事件的最早发生时间。
/* 求入度函数 */
void FindID(AdjList *G, int indegree[]) {
int i;
ArcNode *p;
for (i = 0; i < G->vexnum; ++i)
indegree[i] = 0;
for (i = 0; i < G->vexnum; ++i) {
p = G->vertex[i].firstarc;
while (p != NULL) {
++indegree[p->adjvex];
p = p->nextarc;
}
}
}
int ve[MAX_VERTEX_NUM];
/* 修改后的拓扑排序算法 */
bool TopoOrder(AdjList *G, Stack *T) {
int count, i, j, k;
ArcNode *p;
int indegree[MAX_VERTEX_NUM];
Stack *S;
InitStack(&S);
FindID(G, indegree);
for (i = 0; i < G->vexnum; ++i)
if (indegree[i] == 0)
Push(S, i);
count = 0;
for (i = 0; i < G->vexnum; ++i)
ve[i] = 0;
while (!IsEmpty(S)) {
Pop(S, &j);
Push(T, j); /* 生成逆拓扑序列 */
++count;
p = G->vertex[j].firstarc;
while (p != NULL) {
k = p->adjvex;
if (--indegree[k] == 0)
Push(S, k);
/* 计算最早发生时间 */
if (ve[j] + p->info.weight > ve[k])
ve[k] = ve[j] + p->info.weight;
p = p->nextarc;
}
}
ClearStack(S);
return count >= G->vexnum;
}
3.2 关键路径算法
bool CriticalPath(AdjList *G) {
ArcNode *p;
int i, j, k, dut, ei, li;
char tag;
int vl[MAX_VERTEX_NUM];
Stack *T;
InitStack(&T);
if (!TopoOrder(G, T)) /* 检验TopoOrder是否执行成功 */
return false;
/* 初始化事件的最晚发生时间 */
for (i = 0; i < G->vexnum; ++i)
vl[i] = ve[G->vexnum - 1];
/* 按逆拓扑序列求各顶点的vl值 */
while (!IsEmpty(T)) {
Pop(T, &j);
p = G->vertex[j].firstarc;
while (p != NULL) {
k = p->adjvex;
dut = p->info.weight;
if (vl[k] - dut < vl[j])
vl[j] = vl[k] - dut;
p = p->nextarc;
}
}
/* 求ee、el,并且输出路径 */
for (j = 0; j < G->vexnum; ++j) {
p = G->vertex[j].firstarc;
while (p != NULL) {
k = p->adjvex;
dut = p->info.weight;
ee = ve[j];
el = vl[k] - dut;
/* 标出关键活动并打印路径 */
tag = (ee == el) ? '*' : ' ';
printf("%s,%s,%d,%d,%d,%c\n", Vertex2Name(G->vertex[j].data),
Vertex2Name(G->vertex[k].data), dut, ee, el, tag);
p = p->nextarc;
}
}
ClearStack(T);
return true;
}