//关键路径
//E(i): 事件Vi可能的最早发生时间,它等同于从源点V1到Vi的最长路径长度
//L(i): 在保证结束点Vn在E(n)时刻发生的前提下,事件Vi允许的最晚发生时间,它等于E(n)-从顶点Vi到Vn的最长路径长度
//计算下面这两个两
//1 E(1)=0,然后沿着正向路径求E(j), E(j)=max{E(i)+Wij} , i<j<=n
//把所有的E(1..n) 都求出来.
//2 L(n)=E(n),然后反向求L(i), L(i)=min{L(j)-Wij} , i<j<=n
//关键路径的算法思路:
//1 从源点出发对网中顶点进行拓扑排序,在排序过程中,计算每个顶点的可能的最早发生时间
// 同时随时把入度为0的点的序号入栈,以便在排序结束后得到一个顶点的拓扑逆序.
// 当网中所有顶点都输出后,从保存顶点逆拓扑序列的栈中,依次弹出每个顶点的序号,计算各个顶点的最迟发生事件.以及各个活动的时间余量
//约定:
//1 AOE使用 邻接表
//2 E[1..n] : 每个顶点的最早发生时间
//3 L[1..n] : 每个顶点的最晚发生时间
//4 s 栈,t1 入栈的栈顶, t2 出栈的栈顶
//求关键路径
void criticalpath()
{
float E[n0+1]; //节点的最早发生时间
float L[n0+1]; //节点的最晚发生时间
int s[n0+1]; //栈
int t1 = -1; //入栈栈顶
int t2 = n0+1; //出栈栈顶
arcnode* p;
//求E[1..n]
//初始化
for(i=1;i<=n;i++)
{
E[i]=0;
}
s[++t1]=1; //入度为0的顶点入栈
while(t1 >-1)
{//如果栈不为空
i=s[t1--];//出栈
s[--t2]=i;//逆序的入栈
p=adjlist[i].first; //得到第i个节点关联的边
while(p)
{//如果第i个节点关联的边不为空,把这些边对应的点的入度-1
j=p->vertext; //边对应的点, i,j是有边相连接的
adjlist[j].degree--;
if(adjlist[j].degree == 0)
{//如果j点的入度为0,入t1栈
s[++t]=j;
}
if(E[j]<E[i]+p->weight)
{//得到max的E[j]
E[j] = E[i]+p->weight;
}
p=p->next;
}
}
//到这里构造E[1..n]结束,构造s[t2]结束.
//求L[1..n]
//初始化
for(i=1;i<=n;i++)
{
E[i]=L[n];
}
//一个拓扑逆序的过程
while(t2<n0+1)
{//当t2栈不为空,注意t2栈是一个逆序
i=s[t2++]; //t2栈出栈
p=adjlist[i].first; //这里的adjlist[i] 和上面的 adjlist[j]是 逆序的存储
while(p)
{
j = p->vertext;
if(L[i]>L[j]-p->weight)
{//求最小的
L[i]=L[j]-p->weight;
}
p=p->next;
}
}
//到这里就得到了L[1..n]
//下面输出所有的关键节点
for(i=1;i<=n;i++)
{
p= adjlist[i].first; //p是i节点连接的边
while(p)
{//如果这个边存在,要判断这个边是不是关键边,关键边就是关键路径
j=p->vertext;//j是这个边的另外一个节点,这条边是(i,j)
if(L[j] == E[i]+p->weight)
{//如果最晚结束时间 = 最早开始时间 + 需要时间 -> 这条边就是关键的
cout<<"<"<<i<<","<<j<<">"<<endl;
}
p = p->next;
}
}
}