AOE网
- 在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE网。
- 把AOE网中没有入边的顶点称为始点或源点,没有出边的点称为终点或汇点。由于一个工程总有一个开始,一个结束,所以正常情况下,AOE网只有一个源点,一个汇点。
- AOE网具有明显的工程特性,如在某顶点所代表的事件发生后,从该顶点出发的各活动才能开始。只有在进入某顶点的各活动都已经结束,该顶点所代表的事件才能发生。
- 我们把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。
关键路径算法
实现步骤
我们定义如下几个参数:
- 事件的最早发生时间etv(earliest time of vertex),即顶点Vk的最早发生时间。
- 事件的最晚发生时间ltv(latest time of vertex),即顶点Vk的最晚发生时间。
- 活动的最早发生时间ete(earliest time of edge),即弧ak的最早发生时间。
- 活动的最晚发生时间lte(latest time of edge),即弧ak的最晚发生时间。
1、在求关键路径之前,需要先调用一次拓扑序列算法的代码来计算etv和拓扑序列列表。
2、通过对拓扑序列列表出栈,计算得到ltv。
3、根据etv和ltv计算每个弧的ete和lte,若ete等于lte则该路径为关键路径。
代码实现
/*
* @Author: Xyh4ng
* @Date: 2022-11-24 14:49:32
* @LastEditors: Xyh4ng
* @LastEditTime: 2022-11-24 16:35:29
* @Description:
* Copyright (c) 2022 by Xyh4ng 503177404@qq.com, All Rights Reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 500
#define INFINITY 10000
typedef struct
{
int vexs[MAXSIZE];
int arc[MAXSIZE][MAXSIZE];
int numVertexes, numEdges;
} MGraph;
// 边表结点
typedef struct EdgeNode
{
int adjVex;
int weight;
struct EdgeNode *next;
} EdgeNode;
// 顶点表结点
typedef struct VertexNode
{
int in;
int data;
EdgeNode *firstEdge;
} VertexNode, AdjList[MAXSIZE];
typedef struct GraphAdjList
{
AdjList adjList;
int vertexNums, edgeNums;
} GraphAdjList;
// 构建图
void CreateMGraph(MGraph *G)
{
G->numVertexes = 10;
G->numEdges = 13;
for (int i = 0; i < G->numVertexes; i++)
{
G->vexs[i] = i;
}
for (int i = 0; i < G->numVertexes; i++)
{
for (int j = 0; j < G->numVertexes; j++)
{
if (i == j)
G->arc[i][j] = 0;
else
G->arc[i][j] = INFINITY;
}
}
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;
}
// 利用邻接矩阵构建邻接表
void InitGraphADjList(MGraph *G, GraphAdjList *GL)
{
GL->vertexNums = G->numVertexes;
GL->edgeNums = G->numEdges;
for (int i = 0; i < G->numVertexes; i++) /* 读入顶点信息,建立顶点表 */
{
GL->adjList[i].in = 0;
GL->adjList[i].data = G->vexs[i];
GL->adjList[i].firstEdge = NULL; /* 将边表置为空表 */
}
for (int i = 0; i < G->numVertexes; i++) /* 建立边表 */
{
for (int j = 0; j < G->numVertexes; j++)
{
if (G->arc[i][j] != 0 && G->arc[i][j] < INFINITY)
{
EdgeNode *e = (EdgeNode *)malloc(sizeof(EdgeNode));
e->adjVex = j; /* 邻接序号为j */
e->weight = G->arc[i][j];
e->next = GL->adjList[i].firstEdge; /* 将当前顶点上的指向的结点指针赋值给e */
GL->adjList[i].firstEdge = e; /* 将当前顶点的指针指向e */
GL->adjList[j].in++;
}
}
}
}
int TopologicalSort(GraphAdjList *GL, int *etv, int *stackSort, int *topSort)
{
int count = 0; // 用于统计输出顶点的个数
int *stack = (int *)malloc(sizeof(int) * GL->vertexNums);
int top = 0;
for (int i = 0; i < GL->vertexNums; i++)
{
if (GL->adjList[i].in == 0)
{
stack[++top] = i;
}
}
while (top != 0)
{
int k = stack[top--];
stackSort[++(*topSort)] = k;
count++;
EdgeNode *p = GL->adjList[k].firstEdge;
while (p != NULL)
{
GL->adjList[p->adjVex].in--;
if (etv[k] + p->weight > etv[p->adjVex])
etv[p->adjVex] = etv[k] + p->weight;
if (GL->adjList[p->adjVex].in == 0)
{
stack[++top] = p->adjVex;
}
p = p->next;
}
}
if (count < GL->vertexNums) // 若输出的顶点个数少了,则说明这个网存在环路,不是AOV网
return 0;
else
return 1;
}
void CriticalPath(GraphAdjList *GL, int *etv, int *stackSort, int *topSort)
{
int *ltv = (int *)malloc(sizeof(int) * GL->vertexNums); // 事件最晚开始时间
int ete; // 活动最早开始时间 = 与活动相关的事件的最早开始时间
int lte; // 活动最晚开始时间 = 指向活动的最晚开始时间 - 弧的权值
for (int i = 0; i < GL->vertexNums; i++)
{
ltv[i] = etv[stackSort[(*topSort)]];
}
// 计算各事件的最晚开始时间
while ((*topSort) != 0)
{
int k = stackSort[(*topSort)--];
EdgeNode *p = GL->adjList[k].firstEdge;
while (p != NULL)
{
if (ltv[p->adjVex] - p->weight < ltv[k])
ltv[k] = ltv[p->adjVex] - p->weight;
p = p->next;
}
}
// 计算活动最早开始时间,活动最晚开始时间,寻找关键路径
for (int i = 0; i < GL->vertexNums; i++)
{
ete = etv[i];
EdgeNode *p = GL->adjList[i].firstEdge;
while (p != NULL)
{
lte = ltv[p->adjVex] - p->weight;
if (ete == lte)
{
printf("<V%d - V%d> length: %d\n", GL->adjList[i].data, GL->adjList[p->adjVex].data, p->weight);
}
p = p->next;
}
}
}
int main()
{
MGraph *G = (MGraph *)malloc(sizeof(MGraph));
CreateMGraph(G);
GraphAdjList *GL = (GraphAdjList *)malloc(sizeof(GraphAdjList));
InitGraphADjList(G, GL);
// 为了获得各事件的最早开始时间,需要先对AOE网进行一次拓扑排序
int *etv = (int *)malloc(sizeof(int) * GL->vertexNums); // 事件最早开始时间
for (int i = 0; i < GL->vertexNums; i++)
{
etv[i] = 0;
}
int *stackSort = (int *)malloc(sizeof(int) * GL->vertexNums);
int *topSort = (int *)malloc(sizeof(int));
*topSort = 0;
int flag = TopologicalSort(GL, etv, stackSort, topSort);
if (flag == 1)
CriticalPath(GL, etv, stackSort, topSort);
return 0;
}