概念
AOE网,即边表示活动的网,是一个带权的有向无环图,其中顶点表示事件(Event),每个事件表示在它之前的活动已经完成,在它之后的活动可以开始,弧表示活动,权表示活动持续的时间。
AOE网可用来估算工程的完成时间。由于整个工程只有一个开始点和一个完成点,故在正常的情况(无环)下,网中只有一个入度为零的点(源点)和一个出度为零的点(汇点)。
关键路径: 路径长度最长的路径。
最早发生时间: 假设开始点是v1,从v1到vi的最长路径长度叫做事件vi的最早发生时间。
这个时间决定了所有以vi为尾的弧所表示的活动的最早开始时间。用e(i)表示活动ai的最早开始时间,l(i)为一个活动的最迟开始时间,这是在不推迟整个工程完成的前提下,活动ai最迟必须开始进行的时间。两者之差l(i)-e(i)意味着完成活动ai的时间余量。l(i)=e(i)的活动叫做关键活动。
关键路径上的所有活动都是关键活动,提前完成非关键活动(不在关键路径的活动)并不能加快工程的进度。
求关键路径的算法
- 输入e条弧<j, k>,创建有向图的存储结构。
- 判断是否为AOE网
从源点出发,令ve[0]=0,按拓扑顺序求其余各顶点的最早发生时间ve[i]。
如果得到的拓扑有序序列中顶点个数小于网中顶点数n,则说明网中存在环,不能求关键路径,算法终止,否则转到步骤3。 - 从源点出发,循环遍历每一个结点。在循环中同时遍历邻接表中每一个边所存储指向的节点,并且更新其的ve[i].
注:更新时,比较边的权加上更新结点的前一个结点的ve与 该结点本身的ve大小(全部初始化为0),取最大值。
代码
#include<iostream>
#include<cstdlib>
using namespace std;
typedef struct Edge{
int weight;
int tailNode; //边的末顶点
struct Edge *next;
}Edge;
typedef struct Vertex{
Edge *nextEdge;
int InDegree; //入度
int OutDegree; //出度
int ve;
}Vertex;
typedef struct DirectedGraph{
Vertex *Vex;
int VexNum; //顶点个数
int EdgeNum; //边个数
}DirectedGraph;
typedef struct{
int *Data;
int Top;
int MaxSize;
}Stack;
//函数声明
int Max(int a,int b);
DirectedGraph *CreateDirectedGraph(DirectedGraph *g);
int IfAOE(DirectedGraph *g);
void InitStack(Stack *l,int maxsize);
int Pop(Stack *l);
void Push(Stack *l,int e);
DirectedGraph *CreateDirectedGraph(DirectedGraph *g){
int vexnum,edgenum;
//cout<<"输入顶点个数与边的个数: ";
cin>>vexnum;cin>>edgenum;
g->VexNum=vexnum; g->EdgeNum=edgenum;
g->Vex=new Vertex [g->VexNum];
for(int i=0;i<g->VexNum;i++){
g->Vex[i].InDegree=0;
g->Vex[i].OutDegree=0;
g->Vex[i].ve=0;
g->Vex[i].nextEdge=NULL;
}
for(int k=0;k<g->EdgeNum;k++){
int i,j,w;
cin>>i; cin>>j; cin>>w;
g->Vex[i].OutDegree++;
g->Vex[j].InDegree++;
Edge *em=new Edge; //分配一个结点存储数据
em->weight=w;
em->tailNode=j;
if(g->Vex[i].nextEdge==NULL){
g->Vex[i].nextEdge=em;
em->next=NULL;
}
else{
em->next=g->Vex[i].nextEdge;
g->Vex[i].nextEdge=em;
}
}
return g;
}
int Max(int a,int b){
return a>b?a:b;
}
void VertexEarliest(DirectedGraph *g){
int i;
for(i=0;i<g->VexNum;i++){
Edge *move=g->Vex[i].nextEdge;
while(move!=NULL){
g->Vex[move->tailNode].ve=Max(move->weight+g->Vex[i].ve,g->Vex[move->tailNode].ve);
move=move->next;
}
}
}
int KeyRoute(DirectedGraph *g){
for(int i=0;i<g->VexNum;i++){
if(g->Vex[i].OutDegree==0)
return g->Vex[i].ve;
}
return 0;
}
int IfAOE(DirectedGraph *g){
//若有多颗树
int OriginalPointNUM=0,MeetingPointNUM=0;
for(int i=0;i<g->VexNum;i++){
if(g->Vex[i].InDegree==0) OriginalPointNUM++;
if(g->Vex[i].OutDegree==0) MeetingPointNUM++;
}
if(OriginalPointNUM>1||MeetingPointNUM>1) return 0;
//若有回路
int indegree[g->VexNum]; int i;
for(i=0;i<g->VexNum;i++)
indegree[i]=g->Vex[i].InDegree; //给入度数组赋值
int topo[g->VexNum]; //定义一个拓扑序列数组,存放入度为0的结点
Stack *S=(Stack*)malloc(sizeof(Stack));
InitStack(S,g->VexNum);
for(i=0;i<g->VexNum;i++){
if(!indegree[i]) Push(S,i); //入度为0者进栈
}
int num=0; //对输出顶点计数,初始为0
while(S->Top!=-1){
topo[num]=Pop(S);
Edge *p=g->Vex[topo[num]].nextEdge;
while (p!=NULL){
indegree[p->tailNode]--;
if(indegree[p->tailNode]==0) Push(S,p->tailNode);
p=p->next;
}
num++;
}
if(num<g->VexNum){
return 0;
}
//若是AOE网,"最早发生时间为:"
VertexEarliest(g);
return KeyRoute(g);
}
void InitStack(Stack *l,int maxsize){
l->MaxSize=maxsize;
l->Data=(int *)malloc(l->MaxSize*sizeof(int));
l->Top=-1;
}
void Push(Stack *l,int e){
if(l->Top==l->MaxSize) return;
l->Top++;
*l->Data=e; l->Data++;
}
int Pop(Stack *l){
int a;
if(l->Top==-1) return 0;
int temp=*(--l->Data);
l->Top--;
return temp;
}
int main(){
DirectedGraph *g=new DirectedGraph;
g=CreateDirectedGraph(g);
int a=IfAOE(g);
if(a==0) cout<<"false"<<endl; //如果不为AOE网,输出false
else cout<<a<<endl;
return 0;
}
代码的解释后面有时间再细说,如果有疑问我会尽快解答。