先来说下关键路径的求解思路,引用于百度百科。
1.从开始顶点 v1 出发,令 ve(1)=0,按拓扑有序序列求其余各顶点的可能最早发生时间。
Ve(k)=max{ve(j)+dut(<j,k>)}
, j ∈ T 。其中T是以顶点vk为尾的所有弧的头顶点的集合(2 ≤ k ≤ n)。
如果得到的拓朴有序序列中顶点的个数小于网中顶点个数n,则说明网中有环,不能求出关键路径,算法结束。
2.从完成顶点
出发,令
,按逆拓扑有序求其余各顶点的允许的最晚发生时间:vl(j)=min{vl(k)-dut(<j,k>)}
,k ∈ S 。其中 S 是以顶点vj是头的所有弧的尾顶点集合(1
≤ j ≤ n-1)。
3.求每一项活动ai(1 ≤ i ≤ m)的最早开始时间e(i)=ve(j),最晚开始时间l(i)=vl(k)-dut(<j,k>) 。若某条弧满足 e(i)=l(i) ,则它是关键活动。
#include <cstdio>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#include <iostream>
using namespace std;
const int maxn=1e2+2;
const int INF =0x3f3f3f3f;
vector<int> Gz[maxn],Gn[maxn];
int Mao[maxn][maxn],idx[maxn][maxn];
int indx[maxn],outdx[maxn],vz[maxn],vn[maxn];
int n,m,x,y,val,ans;
struct P
{
int u,v,id;//id用来标记顺序
bool operator<(const P & a)const//这个只是按照题目要求输出
{
if(u<a.u || (u==a.u && id>a.id)) return true;
else return false;
}
}path[maxn*maxn];
void inition()
{
memset(idx,0,sizeof(idx));
memset(indx,0,sizeof(indx));
memset(outdx,0,sizeof(outdx));
memset(Mao,0,sizeof(Mao));
memset(vz,0,sizeof(vz));
memset(vn,INF,sizeof(vn));//最晚发生时间要初始化最大
for(int i=0; i<n; i++) Gz[i].clear();
for(int i=0; i<n; i++) Gn[i].clear();
}
int topoz()
{
int sum=0;
queue<int> q;
for(int i=1; i<=n; i++)//正向拓扑时把所有入度为零的加入队列,即多个起点都加入队列
if(!indx[i])
q.push(i);
while(!q.empty())
{
sum++;
int u=q.front();
q.pop();
for(int i=0; i<Gz[u].size(); i++)//just 拓扑,但是要求出最早发生的时间,并且注意删除边后要判断这个点是否成为新的起点,是就加入队列
{
int v=Gz[u][i];
vz[v]=max(vz[v],vz[u]+Mao[u][v]);
indx[v]--;
if(!indx[v]) q.push(v);
}
}
return sum;
}
void topon()
{
queue<int> q;
for(int i=1; i<=n; i++)//逆序的拓扑和正序道理一样,只是要求解最晚发生时间
{
if(!outdx[i])
{
q.push(i);
vn[i]=ans;
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0; i<Gn[u].size(); i++)
{
int v=Gn[u][i];
vn[v]=min(vn[v],vn[u]-Mao[v][u]);
outdx[v]--;
if(!outdx[v]) q.push(v);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
inition();
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&val);
Mao[x][y]=Mao[y][x]=val;//这里用数组Mao存储边的权值,用vector存储边,只是为了减少不必要的循环
indx[y]++;
outdx[x]++;//记录出度入度
Gz[x].push_back(y);
Gn[y].push_back(x);
idx[x][y]=idx[y][x]=i;//这个地方记录边的顺序
}
if(topoz()!=n) printf("0\n");//如果无法拓扑则说明失败
else
{
ans=0;
for(int i=1; i<=n; i++)
if(!outdx[i])
ans=max(ans,vz[i]);//求解出关键活动的权值
printf("%d\n",ans);
topon();
int cnt=0;
for(int i=1; i<=n; i++)
{
if(vz[i]==vn[i])//当ve和vl值相等,表示此处为关键活动
{
for(int j=1;j<=n;j++)
{
if(vz[j]==vn[j] && idx[i][j] && Mao[i][j]+vz[i]==vz[j])//idx[i][j]表示这两点是连通的,Mao[i][j]+vz[i]=vz[j]保证输出路径是正序
{
path[cnt].u=i;
path[cnt].v=j;
path[cnt].id=idx[i][j];
cnt++;
}
}
}
}
sort(path,path+cnt);
for(int i=0;i<cnt;i++)
printf("%d->%d\n",path[i].u,path[i].v);
}
return 0;
}
本文介绍了一种求解关键路径的方法,通过拓扑排序确定任务的最早开始时间和最晚完成时间,进而找出关键路径。提供了完整的C++代码实现,包括正向和逆向拓扑排序过程。

4198

被折叠的 条评论
为什么被折叠?



