有向无环图计算关键路径的实现
把工程计划表示为边表示的活动的网络,即AOE网,用顶点表示事件,弧表示活动,弧的权表示活动持续的时间。
顶点/事件:表示在它之前的活动已经完成,在它之后的活动可以开始。
代码实现:
#include <iostream>
#include<cmath>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100
//定义无穷大
#define MAXInt 32767
typedef int Status;
typedef char TElemType;
using namespace std;
//定义顶点数据类型
typedef char VerTexType;
//定义边的数据类型
typedef int ArcType;
//定义图的数据结构
typedef struct {
//顶点表
VerTexType vexs[MAXSIZE];
//邻接矩阵
ArcType arcs[MAXSIZE][MAXSIZE];
//当前结点数和当期边数
int vexnum, arcnum;
}AMGraph; //Adjacency(邻接) Matrix Graph
//函数输入一个顶点的值,返回该顶点在顶点表中的下标,不存在则返回-1
int LocateVex(VerTexType x, AMGraph& G) {
for (int i = 0; i < G.vexnum; i++)
{
if (x == G.vexs[i])
{
return i;
}
}
return -1;
}
void createUDN(AMGraph& G) {
//先输入图的顶点数和边数
cout << "请输入要创建的有向网的顶点数和边数:" << "\n";
scanf_s("%d%d", &G.vexnum, &G.arcnum);
//依次输入各个顶点的值
cout << "依次输入各个顶点的值" << "\n";
for (int i = 0; i < G.vexnum; i++) {
cin >> G.vexs[i];
}
//对邻接矩阵进行初始化
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
G.arcs[i][j] = MAXInt;
}
}
cout << "请输入每条边的顶点和权值:输入方式:起点 终点 权值(也就是活动持续时间)" << "\n";
//输入依次输入每一条边的顶点和权值
for (int i = 0; i < G.arcnum; i++)
{
VerTexType a, b;
ArcType weight;
cin >> a >> b>> weight;
//找到输入的顶点a,b在顶点表中的下标
int x = LocateVex(a, G);
int y = LocateVex(b, G);
if (x != -1 && y != -1) {
G.arcs[x][y] = weight;
}
}
}
//输出顶点表以及邻接矩阵
void outPut(AMGraph G) {
cout << "顶点表如下:" << "\n";
for (int i = 0; i < G.vexnum; i++) {
printf("%c ", G.vexs[i]);
}
printf("\n输出邻接矩阵如下\n");
for (int i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
{
printf("%6d", G.arcs[i][j]);
}
printf("\n");
}
}
typedef struct Event {
int index;//存顶点下标
int ve;//index顶点事件最早发生时间
int vl;//index顶点最迟发生时间
}Event, Eventv[MAXSIZE];
typedef struct EventStruct {
Eventv v;
int length;
};
typedef struct activity {
int start;
int end;//弧的起点和顶点
int e;//表示活动的最早开始时间
int l;//表示活动的最迟开始时间
int LsubE;//表示活动的最早开始时间和最迟开始时间的差值
}activity, activityA[MAXSIZE];
typedef struct ActStruct {
activityA act;
int length;
};
//判断顶点下标x是否已经存在event中,如果存在则返回下标,不存在则返回-1
int IsInEvent(EventStruct event, int x) {
for (int i = 0; i < event.length; i++) {
if (event.v[i].index==x)
{
return i;
}
}
return -1;
}
//计算每个顶点的ve,也就是最早开始时间
void eventVe(AMGraph G, EventStruct& event) {
//寻找起点,也就是入度为0的点
for (int i = 0; i < G.vexnum; i++) {
int j = 0;
for (j = 0; j < G.vexnum; j++) {
if (G.arcs[j][i]!=MAXInt)//如果第j行的第i列元素有不为无穷大的,说明入度不为0
{
break;
}
}
//如果第i列的元素全部都为无穷大说明入度为0
if (j == G.vexnum) {
event.v[event.length].index = i;
event.v[event.length].vl = MAXInt;
event.v[event.length++].ve = 0;//设置起点的最早发生时间为0
break;
}
}
//计算每个顶点事件的最早发生时间
int flag = 0;
while (flag<G.vexnum)
{
int index = event.v[flag].index;
for (int i = 0; i < G.vexnum; i++)
{
if (G.arcs[index][i]!=MAXInt)
{
int sum = G.arcs[index][i] + event.v[flag].ve;
int iEventIndex = IsInEvent(event, i);//顶点i的事件在时间表中的下标
if (iEventIndex!=-1)//如果该顶点已经加入过事件表了,就比较查看是否需要更新
{
if (event.v[iEventIndex].ve<sum)//这个顶点前面的其他路径消耗的时间更大,则修改该顶点的最早开始时间
{
event.v[iEventIndex].ve = sum;
}
}
else {//不存在则加入该顶点并且加入最早开始时间
event.v[event.length].index = i;
event.v[event.length].vl = MAXInt;
event.v[event.length++].ve = sum;
}
}
}
flag++;
}
}
//计算每个顶点的vl,也就是最晚开始时间
void eventVl(AMGraph G, EventStruct& event) {
//记录汇点的下标
int jEventIndex = 0;
//寻找终点,也就是出度为0的点
for (int i = 0; i < G.vexnum; i++) {
int j = 0;
for (j = 0; j < G.vexnum; j++) {
if (G.arcs[i][j] != MAXInt)//如果第i行的第j列元素有不为无穷大的,说明出度不为0
{
break;
}
}
//如果第i行的元素全部都为无穷大说明出度为0
if (j == G.vexnum) {
//查找j顶点在事件表中的下标
jEventIndex = IsInEvent(event, i);
event.v[jEventIndex].vl = event.v[jEventIndex].ve;//汇点的最晚发生时间与最早发生时间相同
break;
}
}
//计算非汇点的每个顶点的最晚发生时间,在计算最早发生时间时已经将最晚发生时间初始化为最大值了
int flag = 0;
//用一个数组来存放访问的顺序
int order[MAXInt];
int orderlength = 0;
order[orderlength++] = jEventIndex;
while (flag < G.vexnum)
{
int index = order[flag];
for (int i = 0; i < G.vexnum; i++)
{
if (G.arcs[i][index] != MAXInt) {
int sum = event.v[index].vl - G.arcs[i][index];
//查找i顶点在事件表中的下标
int iIndex = IsInEvent(event, i);
if (sum < event.v[iIndex].vl) {//如果计算出来的最晚发生时间更小则更新最晚发生时间
event.v[iIndex].vl = sum;
order[orderlength++] = iIndex;
}
}
}
flag++;
}
}
void activityCount(AMGraph G, EventStruct event, ActStruct& activity) {
for (int i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
{
if (G.arcs[i][j]!=MAXInt)//两个顶点之间有边
{
//记录这条边的起点和终点
activity.act[activity.length].start = i;
activity.act[activity.length].end = j;
//计算这条边的e和l,即计算这条边表示的活动的最早开始时间和最迟开始时间
activity.act[activity.length].e = event.v[IsInEvent(event, i)].ve;
activity.act[activity.length].l = event.v[IsInEvent(event, j)].vl-G.arcs[i][j];
activity.act[activity.length++].LsubE= event.v[IsInEvent(event, j)].vl - G.arcs[i][j]- event.v[IsInEvent(event, i)].ve;
}
}
}
}
//输出event表查看
void outEvent(EventStruct& e,AMGraph G) {
printf(" 顶点 ve vl\n");
for (int i = 0; i < e.length; i++)
{
printf("%4c %4d %4d\n", G.vexs[e.v[i].index], e.v[i].ve, e.v[i].vl);
}
}
//输出查看活动表
void outActivity(ActStruct activity, AMGraph G) {
printf(" 起点 终点 e l l-e\n");
for (int i = 0; i < activity.length; i++)
{
printf("%4c %4c %4d %4d %4d\n",
G.vexs[activity.act[i].start], G.vexs[activity.act[i].end],
activity.act[i].e, activity.act[i].l, activity.act[i].LsubE);
}
}
int main() {
AMGraph G;
createUDN(G);
outPut(G);
EventStruct e;
e.length = 0;
eventVe(G, e);
eventVl(G, e);
outEvent(e, G);
ActStruct activity;
activity.length = 0;
activityCount(G, e, activity);
outActivity(activity, G);
return 0;
}
测试:
输入上述老师给的图,只是将顶点按顺序改成abcd…: