AOE网上的关键路径
Time Limit: 1000ms Memory limit: 65536K
题目描述
一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图。
AOE(Activity On Edge)网:顾名思义,用边表示活动的网,当然它也是DAG。与AOV不同,活动都表示在了边上,如下图所示:
如上所示,共有11项活动(11条边),9个事件(9个顶点)。整个工程只有一个开始点和一个完成点。即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)。
关键路径:是从开始点到完成点的最长路径的长度。路径的长度是边上活动耗费的时间。如上图所示,1 到2 到 5到7到9是关键路径(关键路径不止一条,请输出字典序最小的),权值的和为18。
输入
输出
示例输入
9 11 1 2 6 1 3 4 1 4 5 2 5 1 3 5 1 4 6 2 5 7 9 5 8 7 6 8 4 8 9 4 7 9 2
示例输出
18 1 2 2 5 5 7 7 9
提示
下面的阐述中,设AOE网的起点为v0终点为vn.
1.关键路径
AOE网中,从事件i到j的路径中,加权长度最大者称为i到j的关键路径(Critical Path),记为cp(i,j)。特别地,始点0到终点n的关键路径cp(0,n)是整个AOE的关键路径。
显然,关键路径决定着AOE网的工期,关键路径的长度就是AOE网代表的工程所需的最小工期。
2.事件最早/晚发生时间
事件vi的最早发生时间ve(i)定义为:从始点到vi的最长(加权)路径长度,即cp(0,i)
事件vi的最晚发生时间vl(i)定义为:在不拖延整个工期的条件下,vi的可能的最晚发生时间。即vl(i) = ve(n) - cp(i, n)
3.活动最早/晚开始时间
活动ak=<vi, vj>的最早开始时间e(k):等于事件vi的最早发生时间,即
活动ak=<vi, vj>的最晚开始时间l(k)定义为:在不拖延整个工期的条件下,该活动的允许的最迟开始时间,即
这里,vl(j)是事件j的允许的最晚发生时间,len(i, j)是ak的权。
活动ak的最大可利用时间:定义为l(k)-e(k)
若活动ak的最大可利用时间等于0(即(l(k)=e(k)),则称ak 为关键活动,否则为非关键活动。
显然,关键活动的延期,会使整个工程延期。但非关键活动不然,只要它的延期量不超过它的最大可利用时间,就不会影响整个工期。
关键路径的概念,也可以用这里的关键活动定义,即有下面的:
(一) 基本算法
根据前面给出的定义,可推出活动的最早及最晚发生时间的计算方法:
结点的最早发生时间的计算,需按拓扑次序递推:
对所有<i,j> ∈E的i
关于
这种计算方法, 依赖于拓扑排序, 即计算ve( j) 前,应已求得j 的各前趋结点的ve值,而计算vl(i)前,应已求得i的各后继结点的vl值。ve的计算可在拓扑排序过程中进行,即在每输出一个结点i后,在删除i的每个出边<i,j>(即入度减1)的同时,执行
实际上,该操作对i的每个后继j分别进行一次。因此对程序作少量扩充即可求得ve。
vl的值可按类似的方法在逆拓扑排序过程(即直接按与拓扑序列相反的次序输出结点的过程)中求得,但一般不必专门这样进行。事实上,通过逆方向使用拓扑序列即可递推出各vl的值,假定拓扑序列是topoSeq,则vl 的值的求法为(结点编号为1~n)。
#include<iostream>
#include<stack>
#include<cstring>
#define MaxN 10010
#define MaxM 50010
using namespace std;
struct node
{
int d,to,on;
node *next;
}*ls1[MaxN],*ls2[MaxN];
int cu1[MaxN],cu2[MaxN];
int Ee[MaxN],El[MaxN];
int e[MaxM],l[MaxM];
int n,m;
void O_TopSort()//正序拓扑
{
memset(Ee,0,sizeof(Ee));
stack<int >s;
for(int i=1; i<=n; i++)
if(!cu1[i])
s.push(i);
int t,k;
node *p;
while(!s.empty())
{
t=s.top();
s.pop();
p=ls1[t];
while(p)
{
k=p->to;
cu1[k]--;
if(cu1[k]==0)
s.push(k);
if(Ee[t]+p->d>Ee[k])
Ee[k]=Ee[t]+p->d;
p=p->next;
}
// ls1[t]=NULL;
}
// for(int i=1; i<=n; i++)
// cout<<Ee[i]<<" ";
}
void R_TopSort()//逆序拓扑
{
memset(El,0,sizeof(El));
stack<int >s;
for(int i=1; i<=n; i++)
{
El[i]=Ee[n];
if(!cu2[i])
s.push(i);
}
int t,k;
node *p;
while(!s.empty())
{
t=s.top();
s.pop();
p=ls2[t];
while(p)
{
k=p->to;
if(--cu2[k]==0)
s.push(k);
if(El[t]-p->d<El[k])
El[k]=El[t]-p->d;
p=p->next;
}
// ls2[t]=NULL;
}
// for(int i=1;i<=n;i++)
// cout<<El[i]<<" ";
}
int main()
{
while(cin>>n>>m)
{
memset(ls1,0,sizeof(ls1));
memset(ls2,0,sizeof(ls2));
memset(cu1,0,sizeof(cu1));
memset(cu2,0,sizeof(cu2));
int a,b,c;
for(int i=1; i<=n; i++)
{
ls1[i]=NULL;
ls2[i]=NULL;
}
for(int i=0; i<m; i++)
{
cin>>a>>b>>c;
node *p=new node;
p->d=c;
p->to=b;
p->on=i;
cu1[b]++;
p->next=ls1[a];
ls1[a]=p;
node *q=new node ;
q->d=c;
q->to=a;
p->on=i;
cu2[a]++;
q->next=ls2[b];
ls2[b]=q;
}
O_TopSort();
cout<<Ee[n]<<endl;
R_TopSort();
memset(e,0,sizeof(e));
memset(l,0,sizeof(l));
node *p;
int k,j;
for(int i=1; i<=n; i++)
{
p=ls1[i];
int Min;
int flag=0;
if(p)
Min=p->to;
while(p)
{
j=p->to;
k=p->on;
e[k]=Ee[i];
l[k]=El[j]-p->d;
if(e[k]==l[k])
{
if(Min>j) //如果关键路径有多条,比较
{
Min=j;
}
flag=1;
}
p=p->next;
}
if(flag)
{
cout<<i<<" "<<Min<<endl;
i=Min-1; //直接跳转到当前关键路径节点的下一节点
ls1[i]=NULL; //将字典序最小的赋值
}
}
}
return 0;
}
还可以用SPFA求最长路径:
如果正向建图的话,比如下图:如果现在终点是6,现在2和3都能使6的距离达到最大且值相同。我们处理的时候会选2,但还是2这条路径却不是最优的,反而3是最优的。
所以我们逆向见图,求一个最短路然后倒着输出就好了。
解决方式:
倒序建图,当松弛时(u,v),遇到相同的情况,尽量使u变的更小,那么最终得到就是最小的字典序。
对于求最长路径,将dis设为-INF,dis[s] = 0
#include<iostream>
#include<queue>
#include<cstring>
#include<limits.h>
#define Max INT_MAX
#define MaxM 50010
#define MaxN 10010
using namespace std;
struct node
{
int to;
int we;
node *next;
} *lis[MaxN];
int path[MaxN];//值为上一节点,下标为下一节点
int dist[MaxN];
int n,m;
void SPFA_BFS(int v0)
{
int v[MaxN];
for(int i=0; i<n; i++)
{
dist[i]=-Max;
path[i]=-1;
v[i]=0;
}
dist[v0]=0;
v[v0]++;
queue<int >q;
while(!q.empty())
q.pop();
q.push(v0);
while(!q.empty())
{
int t=q.front();
q.pop();
v[t]--;
node *tmp=lis[t];
while(tmp)
{
int k=tmp->to;
if(dist[t]+tmp->we>dist[k]||dist[t]+tmp->we==dist[k]&&path[k]>t)//若路径相等,则比较后选择小的
{
dist[k]=dist[t]+tmp->we;
path[k]=t;
if(!v[k])
{
q.push(k);
v[k]++;
}
}
tmp=tmp->next;
}
lis[t]=NULL;
}
}
int main()
{
while(cin>>n>>m)
{
int a,b,c;
memset(lis,0,sizeof(lis));
for(int i=0; i<m; i++)
{
cin>>a>>b>>c;
a--;
b--;
node *q=new node; //倒叙建图
q->we=c;
q->to=a;
q->next=lis[b];
lis[b]=q;
}
SPFA_BFS(n-1);
cout<<dist[0]<<endl;
int t=0;
while(t!=n-1)
{
cout<<t+1<< " "<<path[t]+1<<endl;
t=path[t];
}
}
return 0;
}