求关键路径算法:
①建立AOE网存储结构。
②从源点v0出发,令ve[0]=0,按拓扑有序,求其余各顶点的最早发生时间ve[i]。如果得到的拓扑有序序列中顶点个数小于网中顶点个数n,说明网中有环,算法终止。
③从汇点vn出发,令汇点的vl[n]=ve[n],按逆拓扑有序,求其余各顶点的最迟发生时间vl[i]。
④根据各点ve和vl值,求每条弧s的最早开始时间e和最迟开始时间l。若某弧的e=l,则为关键活动。
1. 邻接矩阵存储结构
//图的邻接矩阵存储表示 #define INFINITY INT_MAX #define MAX_VERTEX_NUM 20 typedef enum {DG, DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网} typedef enum {OK, ERROR} Status; typedef struct ArcCell{ int adj; //顶点关系类型。对于无权图,用0或1表示相邻否。对于有权图,则为权值类型。 string info; //该弧所携带的信息 }ArcCell, AdjMatrix; typedef struct { int vexs[MAX_VERTEX_NUM]; //顶点向量 AdjMatrix arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //邻接矩阵 int vexnum, arcnum; //图的当前顶点数和弧数 GraphKind kind; //图的种类标志 }MGraph; bool visited[MAX_VERTEX_NUM]; //设访问标志数组,供遍历时使用
//求关键路径算法-结构 struct CriticalTime { int ve; //事件的最早发生时间 int vl; //事件的最迟发生时间 }CriTime[MAX_VERTEX_NUM];
2. 构造一个有向网
void CreateDNTest(MGraph &G) { //构造一个有向网 int i,j; G.kind = DN; //数组a[8][3]用来存在有向边的端点和权值 int a[8][3] = { {0,1,3}, {0,2,2}, {1,3,2}, {1,4,3}, {2,3,4}, {2,5,3}, {3,5,2}, {4,5,1} }; G.vexnum = 6; G.arcnum = 8; for(i=0; i<G.vexnum; ++i) G.vexs[i] = i; for(i=0; i<G.vexnum; ++i) for(j=0; j<G.vexnum; ++j) { G.arcs[i][j].adj = INT_MAX; G.arcs[i][j].info = ""; }; for(i=0; i<G.arcnum; ++i) G.arcs[a[i][0]][a[i][1]].adj = a[i][2]; }
3. 求关键路径算法
Status TopologicalSortOrder(MGraph G, LinkStack &T) { //利用拓扑排序求顶点的最早发生时间 //有向图采用邻接矩阵存储,借助堆栈实现 //若G无回路,则输出G的顶点的一个拓扑序列到堆栈T,并返回OK,否则ERROR int i,j; int indegree[MAX_VERTEX_NUM]; //存储各顶点入度 int count=0; //计数已经输出/存储的顶点 LinkStack S; //零入度顶点栈 InitStack(S); InitStack(T); //初始化拓扑序列顶点栈T //初始化ve/vl for(i=0; i<G.vexnum; ++i) { CriTime[i].ve = CriTime[i].vl = 0; } for(i=0; i<G.vexnum; ++i) { //计算各顶点入度 indegree[i] = 0; for(j=0; j<G.vexnum; ++j) if(G.arcs[j][i].adj < INT_MAX && i!=j) indegree[i]++; if(indegree[i]==0) //零入度顶点进栈 Push(S,i); } while(!StackEmpty(S)) { //栈非空 Pop(S, i); Push(T, i); //i号顶点如T栈 ++count; //对输出顶点计数 for(j=0; j<G.vexnum; ++j) if(G.arcs[i][j].adj < INT_MAX) //对顶点i的每个邻接点入度减1 { indegree[j]--; if(indegree[j] == 0) //若入度减为0,则进栈 Push(S, j); //求顶点j的最早发生时间 if(G.arcs[i][j].adj < INT_MAX && CriTime[i].ve+G.arcs[i][j].adj > CriTime[j].ve) CriTime[j].ve = CriTime[i].ve + G.arcs[i][j].adj; } }; if(count<G.vexnum) //该图有有向回路 return ERROR; return OK; } Status CriticalPath(MGraph G) { //输出有向网G的各项关键活动 int i,j,ee,el; //ee,el分别为弧的最早开始时间和最迟开始时间 char tag; LinkStack T; LinkStack temp; InitStack(temp); if(TopologicalSortOrder(G, T) != OK) return ERROR; //如果G中存在环,则ERROR for(i=0; i<G.vexnum; ++i) //初始化事件最迟发生时间 CriTime[i].vl = CriTime[G.vexnum-1].ve; while(!StackEmpty(T)) //按拓扑逆序求各顶点vl值 { Pop(T, j); Push(temp, j); for(i=0; i<G.vexnum; ++i) if(G.arcs[i][j].adj < INT_MAX) if(CriTime[j].vl - G.arcs[i][j].adj < CriTime[i].vl) CriTime[i].vl = CriTime[j].vl - G.arcs[i][j].adj; }; cout<<"关键路径上的顶点:"; for(i=0; i<G.vexnum; ++i) if(CriTime[i].ve == CriTime[i].vl) cout<<i; cout<<endl; while(!StackEmpty(temp)) { Pop(temp, j); for(i=0; i<G.vexnum; ++i) if(G.arcs[j][i].adj < INT_MAX) { ee = CriTime[j].ve; //求该弧的最早开始时间 el = CriTime[i].vl-G.arcs[j][i].adj;//求该弧的最迟开始时间 tag = (ee==el)?'*':' '; //ee=el,则标记为关键活动 cout<<j<<"--"<<i<<":"<<ee<<" "<<el<<tag<<endl; } } return OK; }
4. 主函数
int main() { MGraph gra4; CreateDNTest(gra4); cout<<"关键路径如下(带*):"<<endl; if(CriticalPath(gra4)!=OK) cout<<"该有向图存在环!"<<endl; return 0; }