-
AOE网概念
在⼀一个表示⼯工程的带权有向图中,⽤用顶点表示事件,⽤用有向边表示活动,⽤用边上的权值表示活动 的持续时间,这种有向图的边表表示活动的⽹网,我们称之为AOE ⽹网(Activity On Edge Network)
没有⼊入边的顶点称为始点或源点;
没有出边的顶点称为终点或汇点;
由于⼀一个⼯工程, 总有⼀一个开始,⼀一个结束.所以正常情况下,AOE⽹网只有⼀一个源点和⼀一个汇点. -
AOE网关键名称解释
路路径上各个活动所持续的时间之和称为路路径⻓长度
从源点到汇点具有最⼤大的路路径叫关键路路径
在关键路路径上的活动叫关键活动 -
关键路径算法思路
先定义几个求解关键路径过程中的几个核心参数
- 事件最早发生时间etv,即顶点的最早发生时间,也就是到达这个顶点的最早时间
- 事件最晚发生事件ltv,即顶点的最晚的发生事件,也就是到达这个顶点的最晚时间
- 活动的最早开工时间ete,即弧的最早开工时间
- 活动的最晚开工时间lte,即弧的最晚开工时间
具体思路:首先拓扑排序计算出每个顶点的etv,然后在遍历所有顶点计算出ltv,最后再一次循环算出每个弧的ete和lte如果两者相同说明改弧是关键路径,
-
求解事件最早发生时间etv
按照拓扑排序的顺序求解出每个顶点的etv;
-
求解事件最晚发生时间ltv
这里可以先初始化ltv将每个顶点的ltv设置成汇点的etv,但是要按照拓扑排序的结果倒序求出:
-
实现代码
#include <stdio.h> #include "stdlib.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXVEX 14 /* 最大顶点数,应由用户定义 */ #define MAXEDGE 20 #define INFINITYC 65535 typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef char ElemType; /*邻接矩阵结构 */ typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; typedef struct EdgeNode{ struct EdgeNode *next; int weight; // 权重 int index; //索引 }EdgeNode; typedef struct { int data; int in; // 入度 EdgeNode * firstNode; }vertexNode,AdjList[MAXVEX]; //图结构 typedef struct { AdjList adjList; //图中当前顶点数和边数 int numVertexes,numEdges; }graphAdjList,*GraphAdjList; /*1.构成AOV网图*/ void CreateMGraph(MGraph *G)/* 构件图 */ { int i, j; /* printf("请输入边数和顶点数:"); */ G->numEdges=13; G->numVertexes=10; for (i = 0; i < G->numVertexes; i++)/* 初始化图 */ { G->vexs[i]=i; } for (i = 0; i < G->numVertexes; i++)/* 初始化图 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j]=INFINITYC; } } G->arc[0][1]=3; G->arc[0][2]=4; G->arc[1][3]=5; G->arc[1][4]=6; G->arc[2][3]=8; G->arc[2][5]=7; G->arc[3][4]=3; G->arc[4][6]=9; G->arc[4][7]=4; G->arc[5][7]=6; G->arc[6][9]=2; G->arc[7][8]=5; G->arc[8][9]=3; } /*2.将AOV网图借助邻近矩阵转换成邻接表结构*/ void CreateALGraph(MGraph G,GraphAdjList *GL) { *GL = (GraphAdjList)malloc(sizeof(graphAdjList)); (*GL)->numVertexes = G.numVertexes; (*GL)->numEdges = G.numEdges; //初始化(默认d入度都是0) for(int i = 0; i < G.numVertexes; i ++){ (*GL)->adjList[i].data = G.vexs[i]; (*GL)->adjList[i].in = 0; (*GL)->adjList[i].firstNode = NULL; } EdgeNode *node; for(int i = 0; i < G.numVertexes; i ++){ for(int j = 0; j < G.numVertexes; j ++){ if(G.arc[i][j] < INFINITYC && G.arc[i][j] > 0){ //存在指向关系 node = (EdgeNode *)malloc(sizeof(EdgeNode)); node->index = j; node->weight = G.arc[i][j]; node->next = (*GL)->adjList[i].firstNode; (*GL)->adjList[i].firstNode = node; //对应入度要加一 (*GL)->adjList[j].in++; } } } } /*3.拓扑排序. 若AOV网图无回路则输出拓扑排序的序列并且返回状态值1,若存在回路则返回状态值0*/ /*拓扑排序:解决的是一个工程能否顺序进行的问题!*/ /* 思路:创建一个栈stack 将所有的入度为0的顶点入栈,然后进入下一个阶段先出栈,然后删除栈顶顶元素的所有弧信息,如果有 入度为0 的顶点继续入栈,同时还要记录出栈元素的个数用于判断符合拓扑拍讯(出栈的个数=顶点的个数) **/ //各个顶点的最早发生时间 int *etv; //各个顶点的最晚发生时间 int *ltv; //存储拓扑排序后的结果 int *stack2; //栈顶元素的索引 int top2; Status TopologicalSort(GraphAdjList GL){ //初始化栈 int stack[GL->numVertexes]; int top = 0; //入度为0 的点入栈 for(int i = 0; i < GL->numVertexes; i ++){ if(GL->adjList[i].in == 0){ stack[top ++] = i; } } if(top != 1){ //说明源点没有或者存在多个(AOE网明确源点和汇点只会有一个) return ERROR; } //用于存放拓扑排序后的结果 stack2 = (int *)malloc(sizeof(int) * GL->numVertexes); etv = (int *)malloc(sizeof(int) * GL->numVertexes); top2 = 0; //初始化etv(默认全部是0) for(int i = 0; i < GL->numVertexes; i ++){ etv[i] = 0; } int count = 0; //开始出栈 while (top > 0) { //删除栈顶元素对应的弧信息(也就是对应的顶点的入度减一) printf("%d -> ",stack[--top]); stack2[top2++] = stack[top]; count ++; EdgeNode *node = GL->adjList[stack[top]].firstNode; int currentIndex = stack[top]; while (node) { //求解各定点的etv int k = node->index; if(etv[currentIndex] + node->weight > etv[k]){ etv[k] = etv[currentIndex] + node->weight; } GL->adjList[node->index].in --; if(GL->adjList[node->index].in == 0){ //入栈 stack[top++] = node->index; } node = node->next; } } if(count == GL->numVertexes){ return OK; } return ERROR; } //求关键路径, GL为有向网,则输出G的各项关键活动; void CriticalPath(GraphAdjList GL){ for(int i = 0; i < GL->numVertexes; i ++){ printf("%d ",etv[i]); } printf("\n\n"); //先初始化ltv数组默认每个顶点的额最晚发生时间都是到达汇点的时间 ltv = (int *)malloc(sizeof(int) * GL->numVertexes); for(int i = 0; i < GL->numVertexes; i ++){ ltv[i] = etv[stack2[top2-1]]; } //先求解ltv ltv[0] = 0; EdgeNode * node; while (top2-1 > 0) { node = GL->adjList[stack2[top2-1]].firstNode; while (node) { if(ltv[stack2[top2-1]] > ltv[node->index] - node->weight){ ltv[stack2[top2-1]] = ltv[node->index] - node->weight; } node = node->next; } top2 --; } //打印ltv for(int i = 0; i < GL->numVertexes; i ++){ printf("%d ",ltv[i]); } //开始求解ete(最早开工时间)和lte(最晚开工时间) 如果ete==lte 则说明该路径是最短路径 node = GL->adjList[0].firstNode; int ete,lte; printf("\n\n"); printf("0"); for(int i = 0; i < GL->numVertexes; i ++){ node = GL->adjList[i].firstNode; //最早开工时间(其实就是上一个定点刚刚发生就立马开始,所以就等于上一个顶点的最早发生时间) ete = etv[i]; while (node) { //最晚开工时间(当然就是在不延误工期的情况下最晚发生的时间,r当前定点最晚发生时间减去到达改定需要的权值就好) lte = ltv[node->index] - node->weight; if(ete == lte){ //说明是关键路径 printf("->%d",node->index); } node = node->next; } } } int main(int argc, const char * argv[]) { // insert code here... printf("Hello, World!\n"); GraphAdjList GL; MGraph G; CreateMGraph(&G); CreateALGraph(G, &GL); TopologicalSort(GL); printf("\n\n"); CriticalPath(GL); return 0; }