与AOV网对应的,还有一个AOE网,当然它也是有DAG(向无环图)。
AOE(Activity On Edge)网:顾名思义,用边表示活动的网。
与AOV不同,活动都表示在了边上,如下图所示:
如上所示,共有11项活动(11条边),9个事件(9个顶点)。
整个工程只有一个开始点和一个完成点。
即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)。
(1) 关键路径:是从开始点到完成点的最长路径的长度。路径的长度是边上活动耗费的时间。
(2) 最早发生时间:v1到vi的最长路径叫做vi的最早发生时间,用ei表示。例如上图中,v5的最早发生时间是max(6+1, 4+1) = 7。
(3) 最迟开始时间:在不推迟整个工程完成的前提下,活动i最迟必须开始进行的时间,用l(i)表示。l(i) - e(i)是活动的时间余量。
(4) 关键活动:l(i)=e(i)的活动称为关键活动。
关键路径上的活动都是关键活动。
提前完成非关键活动并不能加快工程进度(类似木桶效应)。
AOE网的主要活动就是识别出哪些是关键活动,以便争取提高关键活动的工效,缩短整个工期。
当然,如果AOE网发生了改变,那么原先的关键路径也会发生变化。
因此,只有在不改变挂件路径的情况下,提供关键活动的速度,才能对整体工程进度起到帮助作用。
求关键路径的算法
求关键路径,就是要求e(i) = l(i)的事件(点)。我们首先必须分别求出所有点的e(i)和l(i)。
由于ve和vl的递推公式必须在拓扑排序下有效,因此,先要求拓扑排序
0、求拓扑排序(同时顺道求ve)
要增设一个栈T记录拓扑有序序列,计算求得各个顶点的ve之后,从栈顶依次弹出栈,就可以倒序遍历,求vl了。
还要使用拓扑排序的那个入度为0的栈,可以每次Pop时候顺道记录下,最后cnt如果<g->nvexs,则表示有环。
1、求最早发生时间ve[]
ve(0)=0开始递推
ve(j) = max{ve{i}+dut(i, j)}
2、求最晚发生时间vl[]
从vl(n-1)=ve(n-1)开始倒推。
vl(j) = min{vl(i) - dut(j, i)}。
3、遍历,求出ve(i) = vl(i)的所有点i。
算法总时间复杂度是O(n+e)。
具体的代码如下:
求拓扑排序和ve、以及栈T
void topo_sort_ve(struct Graph* g, struct Stack* tstk, int* ve) { // Malloc int* indeg = (int*)malloc(sizeof(int)*g->nvexs); int i = 0; int j; int cnt = 0; struct Arc* ptr = NULL; struct Stack* stk = (struct Stack*)malloc(sizeof(struct Stack)); // Init stk stack_init(stk); // Compute indegree memset(indeg, 0, sizeof(int)*g->nvexs); for(i=0; i<g->nvexs; i++) { ptr = g->vexs[i].first_arc; while(ptr!=NULL) { indeg[ptr->ivex]++; ptr = ptr->next; } } // Push all vex that indeg is 0 for(i=0; i<g->nvexs; i++) { if(indeg[i]==0) { if(-1==stack_push(stk, i)) { printf("Stack push fail.\n"); return ; } } } // while ! stk.empty memset(ve, 0, sizeof(int)*g->nvexs); while(!stack_is_empty(stk)) { // Pop if(-1==stack_pop(stk, &j)) { printf("Stack pop fail.\n"); return ; } // Topo stk if(-1==stack_push(tstk, j)) { printf("Stack push fail.\n"); return ; } // Cnt cnt++; // Process all neghbour ptr = g->vexs[j].first_arc; while(ptr!=NULL) { // Push if indegree change to zero if(--indeg[ptr->ivex]==0) { stack_push(stk, ptr->ivex); } // ve[k] = max(e(j) arc(j, k)) if(ve[j] + ptr->w > ve[ptr->ivex]) { ve[ptr->ivex] = ve[j] + ptr->w; } ptr = ptr->next; } } // Free free(indeg); free(stk); // Cnt to see if has cycle if(cnt<g->nvexs) { printf("Has Cycle!\n"); } return ; }
求vl
void topo_vl(struct Graph* g, struct Stack* tstk, int* ve, int* vl) { int i; int j; struct Arc* ptr = NULL; // Pop end point if(-1==stack_pop(tstk, &j)) { printf("Pop fail.\n"); return ; } for(i=0; i<g->nvexs; i++) { vl[i] = ve[j]; } // while ! empty while(!stack_is_empty(tstk)) { // Pop j if(-1==stack_pop(tstk, &j)) { printf("Pop fail.\n"); return ; } // vl[j] = min(vl[k]-arc(j,k)), k is ptr->ivex ptr = g->vexs[j].first_arc; while(ptr!=NULL) { if(vl[ptr->ivex] - ptr->w < vl[j]) { vl[j] = vl[ptr->ivex] - ptr->w; } ptr = ptr->next; } } return ; }
求关键路径
void critical_path(struct Graph* g, int* ve, int* vl) { int i; printf("Critical Path:"); for(i=0;i<g->nvexs;i++) { if(ve[i]==vl[i]) { printf("%d ", g->vexs[i].data); } } printf("\n"); }
其他代码,如创建等,注意我们要存储活动的耗费,给Arc加了一个权值w。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_VEXS 100 #define MAX_QUEUE 1000 // Arc struct Arc { int ivex; // The vex position in vexs array int w; // The data on arc(path) struct Arc* next; }; // Vex struct Vex { int data; struct Arc* first_arc; }; // Graph struct Graph { struct Vex vexs[MAX_VEXS]; int nvexs; int narcs; }; int graph_locate(struct Graph* g, int val) { int i; for(i=0; i<g->nvexs; i++) { if(g->vexs[i].data==val) { return i; } } return -1; } void graph_add_arc(struct Graph* g, int x, int y, int w) { // New arc struct Arc* new_arc = (struct Arc*)malloc(sizeof(struct Arc)); struct Arc* ptr = NULL; if(!new_arc) { return ; } new_arc->ivex = y; new_arc->w = w; new_arc->next = NULL; // Find vexs[x]'s tail if(!g->vexs[x].first_arc) { g->vexs[x].first_arc = new_arc; }else { ptr = g->vexs[x].first_arc; while(ptr->next!=NULL) { ptr = ptr->next; } ptr->next = new_arc; } } void graph_create(struct Graph* g) { int i; int x, y, w; struct Arc* ptr; // Get n vexs printf("Please enter n for number of vexs:\n"); scanf("%d", &g->nvexs); for(i=0; i<g->nvexs; i++) { printf("Please enter a int for vexs(%d):\n", i); scanf("%d", &g->vexs[i].data); g->vexs[i].first_arc = NULL; } // Get m arcs; printf("Please enter m for number of vexs:\n"); scanf("%d", &g->narcs); for(i=0; i<g->narcs; i++) { printf("Please enter x y w for arcs(%d):\n", i); scanf("%d %d %d", &x, &y, &w); x = graph_locate(g, x); y = graph_locate(g, y); if(x==-1 || y==-1) { printf("Wrong x or y for arcs(%d).\n", i); i--; } // arc x->y graph_add_arc(g, x, y, w); } } void graph_print(struct Graph* g) { int i; struct Arc* ptr; // Just print arcs for(i=0;i<g->nvexs;i++) { ptr = g->vexs[i].first_arc; while(ptr) { printf("%d->%d\n", g->vexs[i].data, g->vexs[ptr->ivex].data); ptr = ptr->next; } } } void graph_init(struct Graph* g) { int i; for(i=0; i<MAX_VEXS; i++) { g->vexs[i].first_arc = NULL; } g->nvexs = 0; g->narcs = 0; } void graph_free(struct Graph* g) { int i; struct Arc* ptr; struct Arc* free_node; g->nvexs = 0; g->narcs = 0; for(i=0; i<MAX_VEXS; i++) { ptr = g->vexs[i].first_arc; while(ptr!=NULL) { free_node = ptr; ptr = ptr->next; free(free_node); } } } #define MAX_STACK 100 struct Stack { int top; int data[MAX_STACK]; }; void stack_init(struct Stack* stk) { stk->top = 0; } int stack_push(struct Stack* stk, int v) { if(stk->top>=MAX_STACK) { return -1; }else { stk->data[stk->top++] = v; return 0; } } int stack_pop(struct Stack* stk, int* v) { if(stk->top==0) { return -1; }else { *v = stk->data[--stk->top]; return 0; } } int stack_is_empty(struct Stack* stk) { return stk->top==0; }
测试驱动:
int main() { struct Graph g; struct Stack tstk; int ve[MAX_VEXS]; int vl[MAX_VEXS]; int i; graph_init(&g); graph_create(&g); stack_init(&tstk); topo_sort_ve(&g, &tstk, ve); //for(i=0;i<g.nvexs;i++) //{ // printf("%d\n", ve[i]); //} topo_vl(&g, &tstk, ve, vl); //for(i=0;i<g.nvexs;i++) //{ // printf("%d\n", vl[i]); //} critical_path(&g, ve, vl); graph_free(&g); return 0; }
测试AOE图:
测试数据:
6 1 2 3 4 5 6 8 1 2 3 1 3 2 2 4 2 3 4 4 2 5 3 5 6 1 4 6 2 3 6 3
测试输出:
Critical Path:1 3 4 6