关键路径
1.基本概念
1.AOV网:顶点活动网,是指用顶点表示活动,而用边集表示活动优先关系的有向无环图。
2.AOE网:边活动网,是指用带权的边集表示活动,而用顶点表示事件的有向无环图。
AOV网和AOE网都不能存在环,否则会出现逻辑错误。
3.一般而言,AOV网(AOE网)只有一个源点和一个汇点,即便存在多个源点或汇点,也可以通过添加超级源点和超级
汇点来将它变成只含有一个源点和一个汇点的AOV网(AOE网)
4.AOV转换为AOE:
(1)将AOV的每个顶点都拆成两个顶点,分别表示活动的起点和终点,用有向边连接,有向边表示原顶点的活动,
边权给定。
(2)原AOV网中的边全部视为空活动,边权为0。
5.解决的问题:
a.工程起始到终止至少需要多少时间
b.哪条(些)路径上的活动是影响整个工程的进度的关键
2.关键路径求法
在AOE网中求解。
(1)变量说明:
1.e[]:e数组用来表示活动a的最早开始时间
2.l[]:l数组用来表示活动a的最迟开始时间
3.ve[]:表示事件i的最早发生时间
4.vl[]:表示事件i的最迟发生时间
关键活动:关键路径上的活动。e[r]==l[r]表示活动a[r]是关键活动。
关系:e[r] = ve[i]
l[r] = vl[j]-length[r]
(2)求解每个结点的 ve 和 vl
ve[j] = max{ ve[i1]+length[r1] ~ ve[ik]+length[rk] }:拓扑序
vl[i] = min{ vl[j1]-lenght[r1] ~ vl[jk]-length[rk] }:逆拓扑序
(3)代码实现:
//拓扑序列
stack<int> topOrder;
//拓扑排序,顺便求ve数组
bool topologicalSort(){
queue<int> q;
for(int i=0;i<n;i++){
if(inDegree[i]==0){
q.push(i);
}
}
while(!q.empty()){
int u = q.front();
q.pop();
topOrder.push(u);
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
inDegree[v]--;
if(inDegree[v]==0){
q.push(v);
}
//用ve[u]更新u的所有后继结点v的ve[v]
if(ve[u]+G[u][i].w>ve[v]){
ve[v] = ve[u]+G[u][i].w;
}
}
}
if(topOrder.size()==n) return true;
else return false;
}
//关键路径
int CriticalPath(){
memset(ve,0,sizeof(ve));
if(topologicalSort()==false){
return -1;//不是有向无环图,返回-1
}
fill(vl,vl+n,ve[n-1]);//假设n-1为汇点,vl数组初始化
//直接使用topOrder出栈即为逆拓扑序列,求解vl数组
while(!topOrder.empty()){
int u = topOrder.top();
topOrder.pop();
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
//用u的所有后继结点v的vl[v]来更新vl[u]
if(vl[v]-G[u][i].w<vl[u]){
vl[u] = vl[v]-G[u][i].w;
}
}
}
//遍历邻接表的所有边,计算活动的最早开始事件e和最迟开始时间l
for(int u=0;u<n;u++){
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].w;
int e = ve[u];
int l = vl[v]-w;
if(e==l){
printf("%d->%d",u,v);//输出关键活动
}
}
}
return ve[n-1];
}
如果不确定哪个是汇点,那就取ve数组的最大值,汇点的ve最大。
将以下代码加到上述代码的fill前面。
int maxlength = 0;
for(int i=0;i<n;i++){
if(ve[i]>maxlength){
maxlength = ve[i];
}
}
fill(vl,vl+n;maxlength);
如果图中有多条关键路径,并且要完整输出所有关键路径,可以将关键活动存下来。方法就是建立一个邻接表,
当确定u->v是关键活动时,将边u->v加入邻接表。最后在用DFS遍历这个图就可以获取所有关键路径。