AOE-网如下:
把图中的AOE-网看作一个工程的话,则就需要解决下面两个问题:
- 估算完成整项工程至少需要多少时间
- 判断哪些活动是影响工程进度的关键
分析
整个工程只有一个开始点和一个完成点,所以在正常的情况(无环)下,网中只有一个入度为零的点,称为源点,也只有一个出度为零的点,称为汇点。在整个AOE-网中,一条路径各弧上的权值之和称为该路径的带权路径长度。要估算整项工程完成的最短时间,就是要找一条从源点到汇点的带权路径最长的路径,称为关键路径。关键路径上的活动叫做关键活动,这些活动是影响工程进度的关键,他们提前或拖延将使整个工程提前或拖延。
确定关键路径
一,定义4个描述量
1,事件N的最早发生时间ve[N的下标]
进入事件N的每一活动都结束,N才能发生,所以ve[N的下标]是从源点到N的最长路径长度
2,事件N的最迟发生时间vl[N的下标]
事件N的发生不得延迟于N的每一后继事件的最迟发生时间。
为了不拖延工期,N的最迟发生时间不得迟于其后继事件M的最迟发生事件减去N到M的持续时间
eg:若N是B,M是E;则N的最迟发生时间不得迟于M的最迟发生时间减去MN之间的时间1.
3,活动的最早开始时间e[]
只有事件N发生了,活动x才能发生,所以,活动x的最早开始时间等于事件N的最早发生事件ve[N的下标]
eg:若N是B,x是代表BE这条弧,则只有事件B发生了,活动BE才能发生
4,活动的最晚开始时间l[]
活动x的开始事件需保证不延误事件N的最迟发生时间。所以,活动x的最晚开始时间等于事件N的最迟发生时间vl[N的下标]减去活动x持续的时间
eg:若N是E,x是代表BE这条弧,则BE不能厌恶N的发生,所以最晚开始时间等于vl[N]-BE;
二,关键路径求解过程
- 对图中顶点进行排序,在排序过程中按照拓扑序列求出每个事件的最早发生时间ve[];
- 按照逆拓扑排序求出每个事件的最迟发生时间vl[];
- 求出每个活动XY的最早开始时间e[];
- 求出每个活动XV的最晚开始时间l[];
- 如果e[i]=l[i],即为关键活动。由关键活动形成的由源点到汇点的每一条路径就是关键路径,关键路径可能不止一条
三,关键路径部分求解代码
int ve[pointMax]; //最早发生时间
int vl[pointMax]; //最晚发生时间
void CritPath(ATgroup *A)
{
int n = A->point; //n为顶点个数
for (int i = 0; i < n; i++) //将每个事件的最早事件为0
ve[i] = 0;
//---按拓扑次序求每个事件的最早发生时间---//
int k, j;
for (int i = 0; i < n; i++)
{
k = topo[i]; //取的拓扑排序中的顶点序号
cout <<"K=F"<< k << " "<<endl;
VtNode *p = A->vertices[k].Out; //指向K的第一个邻接结点
while (p != NULL)
{
j = p->peaceE;
if (ve[j] < ve[k] + p->len)
{
ve[j] = ve[k] + p->len;
cout << ve[j] << " " << ve[k] << " " << p->len << endl;
}
p = p->nextVtt;
}
cout << ve[j] << endl;
}
for (int i = 0; i < A->point; i++)
{
cout << ve[i] << " ";
}
cout << endl;
cout << endl;
for (int i = 0; i < n; i++) //初始化
{
vl[i] = ve[topo[n - 1]];
}
//---按逆拓扑排序求每个事件的最迟发生时间----//
for (int i = n - 1; i >= 0; i--)
{
k = topo[i]; //取的拓扑排序中的顶点序号
VtNode *p = A->vertices[k].Out; //指向K的第一个邻接结点
//cout << k << endl;
while (p != NULL)
{
j = p->peaceE;
if (vl[k] > vl[j] - p->len)
{
vl[k] = vl[j] - p->len;
//cout << vl[k] << " " << vl[j] << " " << p->len << endl;
}
p = p->nextVtt;
}
//cout << vl[j] << endl;
}
for (int i = 0; i < A->point; i++)
{
cout << vl[i] << " ";
}
cout << endl;
cout << endl;
//----判断每一活动是否为关键活动-----//
int e, l;
for (int i = 0; i < n; i++)
{
VtNode *p = A->vertices[i].Out;
while (p != NULL)
{
j = p->peaceE;
e = ve[i];
l = vl[j] - p->len;
if (e == l)
{
cout << A->vertices[i].data << " " << A->vertices[j].data << endl;
}
p = p->nextVtt;
}
}
}
四,整段程序代码
#include<iostream>
using namespace std;
#define pointMax 100
struct VtNode //权值信息
{
VtNode *nextVt; //入度链表下一个结点
int peace; //入度下一顶点的值
VtNode *nextVtt; //出度链表下一个结点
int peaceE; //出度下一顶点的值
int len;
};
struct PoNode //顶点信息
{
char data;
VtNode *firstPo; //入度
VtNode *Out; //出度
};
struct ATgroup
{
PoNode vertices[pointMax]; //每一个verticse代表一个顶点
int point, vert; //point顶点数,vert弧数
};
struct Node
{
int data;
Node *next;
};
struct SqStack //栈
{
Node *base; //栈底
Node *top; //栈顶
int data;
};
void Push(SqStack &S, int i) //入栈
{
Node *m = new Node;
m->data = i;
m->next = S.top; //入栈过程
S.top = m;
}
int Pop(SqStack &S) //出栈
{
int n = S.top->data;
S.top = S.top->next; //出栈过程
return n;
}
int ATlocate(ATgroup A, char x) //找到位置
{
for (int i = 0; i < A.point; i++) //依次遍历点的信息
{
if (A.vertices[i].data == x) //找到x的位置
{
return i;
}
}
}
void show(ATgroup &A) //显示当前所有点入度出度的顶点
{
//cout << endl;
for (int i = 0; i < A.point; i++)
{
//cout << i;
if (A.vertices[i].firstPo != NULL) //入度位置
{
// cout << " " << A.vertices[i].firstPo->peace << " ";
}
//else
// cout << " -1" << " ";
if (A.vertices[i].Out != NULL) //出度位置
{
// cout << A.vertices[i].Out->peaceE << endl;
}
//else
// cout << " -1" << endl;
}
}
void CreatAT(ATgroup &A)
{
cout << "输入邻接矩阵顶点数:";
cin >> A.point;
cout << "输入邻接矩阵边数:";
cin >> A.vert;
getchar();
char q[100];
cout << "输入顶点信息:";
gets_s(q);
for (int i = 0; i < A.point; i++)
{
A.vertices[i].data = q[i]; //输入顶点值
A.vertices[i].firstPo = NULL; //初始化头结点为空
A.vertices[i].Out = NULL;
}
char v1, v2; int m, n; int len;
for (int i = 0; i < A.vert; i++) //输入各边,构造邻接表
{
cout << "输入第" << i << "条边的依附的两个顶点:";
int Q;
cin >> v1 >> v2 >> Q;
m = ATlocate(A, v1); //确定位置
n = ATlocate(A, v2);
//第一个
VtNode *p1 = new VtNode;
VtNode *p2 = new VtNode;
p1->peace = m; //入度
p1->nextVt = A.vertices[n].firstPo;
A.vertices[n].firstPo = p1;
p2->peaceE = n; //出度
p2->nextVtt = A.vertices[m].Out;
p2->len = Q;
A.vertices[m].Out = p2;
}
//show(A);
}
void FindIn(ATgroup *A, int *in) //统计所有点的入度数并存入到in数组中
{
int n = 0;
for (int i = 0; i < A->point; i++) //遍历每一个点
{
VtNode *p = new VtNode;
p = A->vertices[i].firstPo;
while (p != NULL) //将入度链表进行遍历
{
n++;
p = p->nextVt; //下一结点
}
in[i] = n; //存入in数组
n = 0;
}
}
void SHOW(int *a, ATgroup *A) //显示当前所有顶点入度数量还有几个
{
for (int i = 0; i < A->point; i++)
{
//cout << a[i] << " ";
}
//cout << endl;
}
int M[pointMax] = { 0 };
int topo[pointMax]; //拓扑遍历存入
void TPsort(ATgroup *A, SqStack &S) //拓扑排序过程
{
int Indegree[pointMax];
FindIn(A, Indegree); //将入度赋值给数组
for (int i = 0; i < A->point; i++)
{
if (Indegree[i] == 0) //将所有入度等于0的入栈
{
//cout << "Push=" << i << endl;
Push(S, i);
}
}
int m = 0; //统计入度的顶点数
int n, k;
while (S.base != S.top) //判断是否遍历完
{
//cout << endl;
n = Pop(S); //栈顶出栈
//cout << "Pop=" << n << endl;
topo[m] = n; //存入topo
m++;
VtNode* p = new VtNode;
p = A->vertices[n].Out; //出度链表的结点
while (p != NULL) //遍历出度链表
{
k = p->peaceE; //某结点的位置
//cout << "出度下一结点k=" << k << endl;
Indegree[k]--; //将该结点顶点位置入度减一
//SHOW(Indegree, A); //显示当前所有点的入度
if (Indegree[k] == 0) //当等于0时,入栈
{
//cout << "Push=" << k << endl;
Push(S, k);
}
p = p->nextVtt; //下一个
}
}
}
int ve[pointMax]; //最早发生时间
int vl[pointMax]; //最晚发生时间
void CritPath(ATgroup *A)
{
int n = A->point; //n为顶点个数
for (int i = 0; i < n; i++) //将每个事件的最早事件为0
ve[i] = 0;
//---按拓扑次序求每个事件的最早发生时间---//
int k, j;
for (int i = 0; i < n; i++)
{
k = topo[i]; //取的拓扑排序中的顶点序号
cout <<"K=F"<< k << " "<<endl;
VtNode *p = A->vertices[k].Out; //指向K的第一个邻接结点
while (p != NULL)
{
j = p->peaceE;
if (ve[j] < ve[k] + p->len)
{
ve[j] = ve[k] + p->len;
cout << ve[j] << " " << ve[k] << " " << p->len << endl;
}
p = p->nextVtt;
}
cout << ve[j] << endl;
}
for (int i = 0; i < A->point; i++)
{
cout << ve[i] << " ";
}
cout << endl;
cout << endl;
for (int i = 0; i < n; i++) //初始化
{
vl[i] = ve[topo[n - 1]];
}
//---按逆拓扑排序求每个事件的最迟发生时间----//
for (int i = n - 1; i >= 0; i--)
{
k = topo[i]; //取的拓扑排序中的顶点序号
VtNode *p = A->vertices[k].Out; //指向K的第一个邻接结点
//cout << k << endl;
while (p != NULL)
{
j = p->peaceE;
if (vl[k] > vl[j] - p->len)
{
vl[k] = vl[j] - p->len;
//cout << vl[k] << " " << vl[j] << " " << p->len << endl;
}
p = p->nextVtt;
}
//cout << vl[j] << endl;
}
for (int i = 0; i < A->point; i++)
{
cout << vl[i] << " ";
}
cout << endl;
cout << endl;
//----判断每一活动是否为关键活动-----//
int e, l;
for (int i = 0; i < n; i++)
{
VtNode *p = A->vertices[i].Out;
while (p != NULL)
{
j = p->peaceE;
e = ve[i];
l = vl[j] - p->len;
if (e == l)
{
cout << A->vertices[i].data << " " << A->vertices[j].data << endl;
}
p = p->nextVtt;
}
}
}
int main()
{
ATgroup *A = new ATgroup;
SqStack *S = new SqStack;
S->top = S->base;
S->data = pointMax;
CreatAT(*A);
TPsort(A, *S);
CritPath(A);
system("pause");
}