相关概念:
(1)AOE (Activity On Edges)网络 如果在无有向环的带权有向图中用有向边表示一个工程中的各项活动(Activity),用边上的权值表示活动的持续时间(Duration),用顶点表示事件(Event),则这样的有向图叫做用边表示活动的网络,简称AOE (Activity On Edges)网络。AOE网是一个带权的有向无环图。AOE网络在某些工程估算方面非常有用。例如,可以使人们了解:
a、完成整个工程至少需要多少时间(假设网络中没有环)?
b、为缩短完成工程所需的时间, 应当加快哪些活动?
(2)关键路径(Critical Path) 在AOE网络中, 有些活动顺序进行,有些活动并行进行。从源点到各个顶点,以至从源点到汇点的有向路径可能不止一条。这些路径的长度也可能不同。完成不同路径的活动所需的时间虽然不同,但只有各条路径上所有活动都完成了,整个工程才算完成。因此,完成整个工程所需的时间取决于从源点到汇点的最长路径长度,即在这条路径上所有活动的持续时间之和。这条路径长度最长的路径就叫做关键路径(Critical Path)。
(3)由于实际工程只有一个开始点和一个结束点,因此AOE网存在唯一的入度为0的开始点(又称源点)和唯一的出度为0的结束点(又称汇点)
参数定义:
(1)事件的最早发生时间 etv(earliest time of vertex):即顶点Vk的最早发生时间。
(2)事件的最晚发生时间 ltv(latest time of vertex):即顶点Vk的最晚发生时间,也就是每个顶点对应的事件最晚需要开始的时间,超出此事件将会延误整个工期。
(3)活动的最早开工时间 ete(earliest time of edge):即弧ak的最早发生时间。
(4)活动的最晚开工时间 lte(latest time of edge):即弧ak的最晚发生时间,也就是不推迟工期的最晚开工时间。
代码实现:
#include <stdio.h> #include <stdlib.h> #define MAXVERTEX 20 #define INCSIZE 10 typedef int ElemType; typedef char VertexType; typedef int EdgeType; typedef struct EdgeNode { int adjvex; EdgeType weight; struct EdgeNode *next; }EdgeNode; typedef struct VertexNode { VertexType data; int in; struct EdgeNode *first; }VertexNode,AdjList[MAXVERTEX]; typedef struct AdjGraphList { AdjList adjList; int numVertex; int numEdge; }AdjGraphList; typedef struct SqStack { ElemType *base; ElemType *top; int stackSize; }SqStack; typedef struct SqStack *LinkStack; static int etv[MAXVERTEX]; //顶点(事件)最早开始时间 static int ltv[MAXVERTEX]; //顶点(事件)的最晚开始时间 static int ete; //弧(活动)的最早开始时间 static int lte; //弧(活动)的最晚开始时间 //建立一个栈 void InitStack(SqStack *s) { s->base = (ElemType *)malloc(MAXVERTEX*sizeof(ElemType)); if(!s->base) return; s->top = s->base; s->stackSize = MAXVERTEX; } void Push(SqStack *s,ElemType *e) { if(s->top - s->base >= s->stackSize - 1) { s->base = (ElemType *)realloc(s->base,(s->stackSize + INCSIZE)*sizeof(ElemType)); if(!s->base) return; } *(s->top) = *e; s->top++; } void Pop(SqStack *s,ElemType *e) { if(s->top == s->base) { return; } s->top--; *e = *(s->top); } int StackLen(SqStack *s) { return (s->top - s->base); } void CreateAdjGraphList(AdjGraphList *G) //构造图,输入图中数据 { int i = 0,j = 0,k = 0,w = 0; int indegree; EdgeNode *p; VertexType c; printf("请输入顶点数和边数,中间用逗号隔开:\n"); scanf("%d,%d",&G->numVertex,&G->numEdge); fflush(stdin); printf("请输入各个顶点存放的值,以及他们的入度,输入#表示结束 :\n"); scanf("%c,%d",&c,&indegree); while(i < G->numVertex) { if(c == ‘#‘) break; G->adjList[i].data = c; G->adjList[i].in = indegree; G->adjList[i].first = NULL; i++; fflush(stdin); printf("请输入各个顶点存放的值,以及他们的入度,输入#表示结束 :\n"); scanf("%c,%d",&c,&indegree); } fflush(stdin); for(k = 0;k < G->numEdge;k++) { printf("请输入边<Vi-Vj>所依附顶点的下标 i 和 j,以及权值w:\n"); scanf("%d,%d,%d",&i,&j,&w); p = (EdgeNode *)malloc(sizeof(EdgeNode)); p->adjvex = j; p->weight = w; p->next = G->adjList[i].first; G->adjList[i].first = p; } } void Findetv(AdjGraphList *G,SqStack *s1,SqStack *s2) { int i,k,count = 0; ElemType e; EdgeNode *p; // SqStack s1,s2; // InitStack(&s1); // InitStack(&s2); for(i = 0;i < G->numVertex;i++) //初始化etv = 0 { etv[i] = 0; if(G->adjList[i].in == 0) { Push(s1,&i); } } while(StackLen(s1)) { Pop(s1,&e); count++; Push(s2,&e); //压入栈s2 p = G->adjList[e].first; while(p) { k = p->adjvex; G->adjList[k].in = G->adjList[k].in -1; if(G->adjList[k].in == 0) { Push(s1,&k); } if(etv[k] < etv[e] + p->weight) //构造数组etv { etv[k] = etv[e] + p->weight; } p = p->next; } } printf("\n\netv数组为:\n"); //测试 for(i = 0;i < G->numVertex;i++) { printf("%d ",etv[i]); } printf("\n"); if(count != G->numVertex) { printf("存在回路,无法找到关键路径!\n"); return; } else { return; } } void FindCriticalPath(AdjGraphList *G,SqStack *s2) { int i = 0,k = 0; ElemType e; EdgeNode *p,*q; Pop(s2,&e); for(i = 0;i < G->numVertex;i++) //初始化顶点最晚开始时间 { ltv[i] = etv[e]; //拓扑排序中最晚弹出的数据的etv最大,也就是完成这个有向无环图所用的时间 } Push(s2,&e); while(StackLen(s2)) { Pop(s2,&e); p = G->adjList[e].first; while(p) { k = p->adjvex; if( ltv[e] > ltv[k] - p->weight ) { ltv[e] = ltv[k] - p->weight; } p = p->next; } } printf("\nltv数组为:\n"); for(i = 0;i < G->numVertex;i++) { printf("%d ",ltv[i]); } printf("\n\n关键路径包含的弧如下: \n"); for(i = 0;i < G->numVertex;i++) { q = G->adjList[i].first; while(q) { k = q->adjvex; ete = etv[i]; //由于顶点(事件)的持续过程为0,所以顶点的最早开始时间也是以这个顶点为弧尾的弧的最早开始时间 lte = ltv[k] - q->weight; if(ete == lte) { printf("<%c,%c> 权值: %d \n",G->adjList[i].data,G->adjList[k].data,q->weight); } q = q->next; } } } int main() { AdjGraphList G; SqStack s1,s2; InitStack(&s1); InitStack(&s2); CreateAdjGraphList(&G); Findetv(&G,&s1,&s2); FindCriticalPath(&G,&s2); return 0; }