引入
生活中往往有着这样的场景,我们想做一件事情,但是需要做其他的事情来达到这件事情,例如,学数据结构之前,首先需要学一门程序设计语言,还要学习离散数学,如果学语言需要耗费半年时间,学离散数学需要一年事件,那么我们能学数据结构需要的最短时间是一年
- 但是,这样的场景往往出现在工程项目中,往往有许许多多的事件和活动,我们需要做的是找到能够影响整个工程进行时间的那一条路径,这个过程类似于木桶原理,这条路径应该是最长的那一条,这样他进行的时间就决定了工程的时间,这样一条路径就是这个工程网络的关键路径
- 我们需要获取这样一条路径来告诉项目经理应该着重关心哪些事件
- 我们也可以利用它去估算工程的完成时间
AOE网的判断
- 什么叫AOE网
AOE网(average of edge),也就是用边去表示活动的网,它是一种带权的有向无环图,其中,顶点表示事件,弧表示活动,权表示活动持续的时间 - 要想说明一个网络是否是AOE网,最重要的是判断是否有环,因为如果出现环,就说明一个事件以自己为先决条件,这是荒谬的
- 判断图中是否出现环,最常规的方法是采用拓扑排序
拓扑排序
- 这个排序的过程是简单的,首先要找到一个无前驱的点,这就是拓扑序列的第一个点
- 接着,从图中删除此点和以它为尾的弧
- 巩固一下知识点,如果<x,y>表示从顶点x到y的一条弧,那么x叫做弧尾或起始点,y叫做弧头或终端点
- 重复刚才的步骤,直至图中不存在无前驱的点,这个时候看图中是否还剩下点,如果还有,说明图中有环;否则,找到的这些点就构成了这个图的拓扑序列,图中无环
template<class TypeOfVer,class TypeOfEdge>
bool adjlist_graph<TypeOfVer,TypeOfEdge>::TopSort(){
if(GraphKind == "UDG"||GraphKind =="UDN") return false;
queue<TypeOfEdge> q;
int inDegree[Vers];
int vis[Vers];
int topsort[Vers];
for(int i=0;i<Vers;i++){
int f = Get_InDegree(i);
if(f == -1) inDegree[i] = 0;
else inDegree[i] = f;
vis[i] = 0;
if(inDegree[i] == 0) q.push(i);
}
edgeNode<TypeOfEdge> *p;
int num = 0;
while(!q.empty()){
int x = q.front();
q.pop();
vis[x] = 1;
topsort[num] = x;
num++;
p = verList[x].head;
while(p){
if(inDegree[p->data]) inDegree[p->data]--;
if(!vis[p->data]&&inDegree[p->data] == 0) q.push(p->data);
p = p->next;
}
}
// for(int i=0;i<num;i++){
// if(i) cout<<"->";
// cout<<verList[topsort[i]].ver;
// if(i == num-1) cout<<endl;
// }
if(num == Vers) return true;
return false;
}
相关函数参见邻接表ADT
事件的最早发生时间和最晚发生时间
如上图,所有的a即表示活动,所有的v表示事件
- 我们使用两个数组,ve[]表示事件的最早发生时间,vl[]表示事件的最晚发生时间
- 事件v0显然最早发生时间为0,那么事件v1需要在v0发生以后才能发生,所以最早发生时间应该为6,v2、v3与之相同,那么v4呢,它取决于v1和v2的完成时间,必须他们都完成v4才能够开始,所以v4的最早发生时间应该为更大的7,到这里我们就可以知道该怎么做了,直接利用前一个活动递推下一个活动时间,需要注意的是最早发生时间应该取较大的时间
- 那么最晚发生时间呢?何为最晚,应该是说它的完成时间不应该影响到后一个事件的完成时间,也就是确保工程能完成那么显然应该从后往前看,并且最后一个最晚发生时间应该与其最早发生时间相同,我们继续算一下,首先v8最晚发生时间为18,求v6、v7都是容易的,那么v4呢?因为它是从两条路径过来的,为了不拖延它之后的两个事件,那么我们应该取较小的那一个时间,因为注意这都是最晚完成时间,如果你取大的,小的那一个事件就完不成,那么最终事件就不能完成,仔细想一下就可以明白
for(int i=0;i<Vers;i++) ve[i] = 0;
for(int i=0;i<Vers;i++){
int k = topSort[i];
edgeNode<int> *p = verList[k].head;
while(p){
int j = p->data;
if(ve[j]<ve[k]+p->weight){
ve[j] = ve[k]+p->weight;
}
p=p->next;
}
}for(int i=0;i<Vers;i++) vl[i] = ve[Vers-1];
for(int i=Vers-1;i>=0;i--){
int k = topSort[i];
edgeNode<int> *p = verList[k].head;
while(p){
int j = p->data;
if(vl[k]>vl[j]-p->weight) vl[k] = vl[j]-p->weight;
p=p->next;
}
}
活动的最早开始时间和最晚开始时间
- 首先活动怎么表示?应该用<x,y>也就是一条弧,那么最早开始时间显然应该是事件x的最早发生时间,最晚发生时间应该是y的最晚发生时间再去掉边权,因为是开始的时间
for(int i=0;i<Vers;i++){
edgeNode<int> *p = verList[i].head;
while(p){
int j = p->data;
int e = ve[i];
int l = vl[j]-p->weight;
cout<<'<'<<verList[i].ver<<','<<verList[j].ver<<'>'<<'\t'<<ve[i]<<'\t'<<l<<endl;
p=p->next;
}
}