AOE网
在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间,称这样的有向图叫做边表示活动的网,简称AOE网。AOE网中没有入边的顶点称为始点(或源点),没有出边的顶点称为终点(或汇点)。
AOE网的性质:
⑴ 只有在某顶点所代表的事件发生后,从该顶点出发的各活动才能开始;
⑵ 只有在进入某顶点的各活动都结束,该顶点所代表的事件才能发生。
**关键路径:**在AOE网中,从始点到终点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。
关键活动:关键路径上的活动称为关键活动。关键活动:e[i]=l[i]的活动
由于AOE网中的某些活动能够同时进行,故完成整个工程所必须花费的时间应该为始点到终点的最大路径长度。关键路径长度是整个工程所需的最短工期。
与关键活动有关的量:
- 事件的最早发生时间earlytime[k]
earlytime[k]是之从始点开始到顶点k的最大路径长度。
earlytime[1]=0
earlytime[k]=max{earlytime[j]+cost<vj,vk>}
2 事件的最迟发生时间lasttime[k]
lasttime[k]是指在不推迟整个工期的前提下,事件k允许的最晚发生时间。
lasttime[n]=earlytime[n] (n为拓扑序列中最后一个顶点)
3.活动的持续时间
4.机动时间(有机动时间的边,说明中间可以有休息,就不是关键路径)
5 活动的最早开始时间e[i]
若活动ai是由弧<vk , vj>表示,则活动ai的最早开始时间应等于事件vk的最早发生时间。因此,有:e[i]=earlytime[k]
6 活动的最晚开始时间l[i]
活动ai的最晚开始时间是指,在不推迟整个工期的前提下, ai必须开始的最晚时间。若ai由弧<vk,vj>表示,则ai的最晚开始时间要保证事件vj的最迟发生时间不拖后。因此,有:l[i]=lasttime[j]-cost<vk, vj>
此图中若4的开始必须依赖5的结束,则添加一条5指向4的权为0的边(图中虚线)
下面的程序中有此图的数据
求关键路径的步骤
- 输入E条弧,建立AOE网络
- 从原点n(开始点)出发,令earlytime[n]=0,安托普有序求其余各顶点的最早发生时间earlytime。如果拓扑有序的顶点数小于网中的顶点数,说明网中存在环,不存在关键路径,算法终止:否则执行步骤三
- 从汇点z(终止点)出发,令lasttime[z]=earlytime[z],按照逆拓扑有序求其余的定点的最迟发生时间
- 根据各顶点的最早发生时间和最迟发生时间,求每条弧的最早开始时间e(s)和最迟开始时间l(s),若某条弧满足条件e(s)==l(s),则为关键活动,或者机动时间为0也是关键活动
#pragma once
#include<iostream>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
struct Ver//顶点
{
int num;
int lasttime;//最迟发生时间
int earlytime;//最早发生时间
Ver() { num = lasttime = earlytime = 0; }
Ver(int a) { num = a; }
Ver(int a, int b, int c) :num(a), lasttime(b), earlytime(c) {}
};
struct Edge
{
int to;//目的顶点
int cost;//时间的持续时间,即边的长度
int d;//机动时间
int lasttime;//最迟开始时间
int earlytime;//最早开始时间
Edge(int a = 0, int b = 0) :to(a), cost(b) { d = cost; }
};
int V;
Ver v[1001];
int in[1001] = { 0 };
vector<Edge>ver[1001];
int E;
bool Toop(stack<int>&st)//拓扑排序求各个顶点的最早发生时间
{
stack<int>ding;//拓扑排序使用
int tot = 0;
for (int i = 1; i <= V; i++)
{
if (in[i] == 0)ding.push(i);//进行拓扑排序,找到入度为0的点入栈
}
while (!ding.empty())
{
int ji = ding.top();
ding.pop();
st.push(ji);//记录逆拓扑排序
tot++;
int len = ver[ji].size();
for(int i=0;i<len;i++)
{
int to = ver[ji][i].to;
int cost = ver[ji][i].cost;
in[to]--;//删边
if (in[to] == 0)ding.push(to);//若入度为0,则入栈
v[to].earlytime = max(v[to].earlytime, v[ji].earlytime + cost);//计算各个顶点的最早发生时间
}
}
if (tot < V)return false;//拓扑排序的顶点数小于网中的顶点数,说明网中存在环
return true;
}
bool GetKeyRoad()
{
stack<int>st;
if (!Toop(st)) {
cout << "此图有环,不存在关键路径\n"; return false;
}
for (int i = 1; i <= V; i++)v[i].lasttime = 1<<30;
v[st.top()].lasttime = v[st.top()].earlytime;//将终点的最迟发生时间赋值为最早开始时间
while (!st.empty())//根据逆拓扑有序计算各个顶点的最早开始时间
{
int ji = st.top();
st.pop();
int len = ver[ji].size();
for (int i = 0; i < len; i++)
{
int to = ver[ji][i].to;
int cost = ver[ji][i].cost;
v[ji].lasttime = min(v[ji].lasttime,v[to].lasttime - cost);//计算各个顶点的最早开始时间
}
}
for (int i = 1; i <= V; i++)
{
int len = ver[i].size();
for (int j = 0; j < len; j++)
{
int to = ver[i][j].to;
int cost=ver[i][j].cost;
ver[i][j].d = v[to].lasttime - v[i].earlytime - cost;//计算各条边的机动时间,(v[to].lasttime - cost为边的最迟开始时间)
if (ver[i][j].d <=0)//若没有机动时间则为关键活动
{
cout << i << " ->" << ver[i][j].to << endl;
}
}
}
return true;
}
void KeyRoad()
{
cout << "请输入顶点数和边数:\n";
cin >> V >> E;
int from, to, cost;
for (int i = 1; i <= V; i++)v[i].num = i;
for (int i = 0; i < E; i++)
{
cin >> from >> to >> cost;
in[to]++;
Edge t(to, cost);
ver[from].push_back(t);
}
GetKeyRoad();
}
/*
数据
由于程序的顶点是从1开始计算,而图中的顶点是从0开始计算
所以把图中的每个顶点加一
9
12
6 5 0
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
*/
参考资料:
https://www.cnblogs.com/navorse/articles/1893863.html
MOOC数据结构(浙江大学)
数据结构(C语言版)–清华大学出版社