前言
在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示该活动持续的时间,则这样的图称为AOE网(Activity On Edge NetWork)。(注意:之前的有向图都是用顶点代表活动的,而AOE网是用边代表活动)
从源点(入度为0)到汇点(出度为0)的所有路径中,具有最大路径长度的路径称为关键路径。把关键路径上的活动称为关键活动。对于工程中非重要的活动,可以允许些许拖延。而对于工程中重要的活动却是不允许拖延的,因为最早开始时间与最晚开始时间相同,因为关键活动的拖延会影响整个工程的完成时间。
这样一来,就将求关键路径转换为求每个活动的最早开始时间和最晚开始时间。之后判断二者是否相等,便可获得关键路径上的关键活动。那么, 如何求最早开始时间和最晚开始时间呢?
①对于一个活动而言,其全部先序活动的最晚完成时间便是该活动的最早开始时间。
②对于一个活动而言,其全部后序活动的最早开始时间减去该活动需要花费的时间,便是该活动的最晚开始时间。
那么如何确定活动的先后次序呢?这时需要用到拓扑排序。
先来看例题,更好进行理解:
例题1
阿里本学期修了“计算机组成原理”这门课。他了解到指令之间可能存在依赖关系。如果两个指令之间的距离(定义为它们的开始时间之差)小于安全距离,那么这可能会导致错误的结果。因此我们需要添加空指令(无用指令)来增加无用操作以确保两条指令之间的距离不小于安全距离。
你的工作是重新排序指令,以便CPU(每个指令的执行时间为1ns)可以使用最短的时间完成所有指令。
输入
输入包含多组。每组:
第一行:N(指令数),M(指令关系数)
接下来M行:X(指令),Y(指令),Z(安全距离) (指令编号0~N-1)
输出
CPU完成所有指令的最短时间
样例输入
5 2
1 2 1
3 4 1
样例输出
2
思路
若将指令的先后依赖关系抽象成有向边,则这个实际问题就转化为判断图上最长路径的长度,即关键路径长度。可以根据拓扑序列逐一求出每个活动的最早开始时间,再根据拓扑序列的逆序列求出每个活动的最晚开始时间。所有活动中最早开始时间和最晚开始时间相同的活动为关键活动,而所有活动中最早开始时间的最大值便是关键路径的长度。
注意,题中指出每个指令的执行时间为1ns,也就是说,所有源点的最早开始时间应该初始化为1,而非通常的0。
(对于最早开始时间的初始化,如果题目没有特殊要求,那么所有结点都初始化为0。对于最晚开始时间的初始化,如果题目没有特殊要求,那么汇点的最晚开始时间初始化为它的最早开始时间,其余结点初始化为INF。)
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#include<climits>
using namespace std;
const int MAXN=1001;
const int INF=INT_MAX;
struct Edge{
int to;
int length;
Edge(int t,int l):to(t),length(l){}
};
vector<Edge> graph[MAXN];
int earliest[MAXN];
int latest[MAXN];
int inDegree[MAXN];
void CriticalPath(int n)
{
vector<int> topology;
queue<int> node;
for(int i=0;i<n;i++)
{
if(inDegree[i]==0)
{
node.push(i);
earliest[i]=1;//初始化为1
}
}
while(!node.empty())
{
int u=node.front();
topology.push_back(u);
node.pop();
for(int i=0;i<graph[u].size();i++)
{
int v=graph[u][i].to;
int l=graph[u][i].length;
earliest[v]=max(earliest[v],earliest[u]+l);
inDegree[v]--;
if(inDegree[v]==0)
node.push(v);
}
}
for(int i=topology.size()-1;i>=0;i--)
{
int u=topology[i];
if(graph[u].size()==0)
latest[u]=earliest[u];
else
latest[u]=INF;
for(int j=0;j<graph[u].size();j++)
{
int v=graph[u][j].to;
int l=graph[u][j].length;
latest[u]=min(latest[u],latest[v]-l);
}
}
}
int main()
{
int n,m;
while(cin>>n>>m)
{
memset(graph,0,sizeof(graph));
memset(earliest,0,sizeof(earliest));
memset(latest,0,sizeof(latest));
memset(inDegree,0,sizeof(inDegree));
while(m--)
{
int from,to,length;
cin>>from>>to>>length;
graph[from].push_back(Edge(to,length));
inDegree[to]++;
}
CriticalPath(n);
int answer=0;
for(int i=0;i<n;i++)
answer=max(answer,earliest[i]);
cout<<answer<<endl;
}
return 0;
}
例题2
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#include<climits>
using namespace std;
const int MAXN=1e5+7;
const int INF=INT_MAX;
const int MOD=1e9+7;
vector<int> graph[MAXN];
int inDegree[MAXN];
long long earliest[MAXN];
long long latest[MAXN];
long long Time[MAXN];
long long CriticalPath(int n)
{
vector<int> topology;
queue<int> node;
for(int i=1;i<=n;i++)
{
if(inDegree[i]==0)
{
node.push(i);
}
}
long long totalTime=0;
while(!node.empty())
{
int u=node.front();
topology.push_back(u);
node.pop();
for(int i=0;i<graph[u].size();i++)
{
int v=graph[u][i];
earliest[v]=max(earliest[v],earliest[u]+Time[u]);
inDegree[v]--;
if(inDegree[v]==0){
node.push(v);
totalTime=max(totalTime,earliest[v]+Time[v]);
}
}
}
for(int i=topology.size()-1;i>=0;i--)
{
int u=topology[i];
if(graph[u].size()==0)
latest[u]=totalTime-Time[u];//注意不是latest[u]=earliest[u],因为汇点不止一个
else
latest[u]=INF;
for(int j=0;j<graph[u].size();j++)
{
int v=graph[u][j];
latest[u]=min(latest[u],latest[v]-Time[u]);
}
}
return totalTime;
}
int main()
{
int n,m;
while(cin>>n>>m)
{
memset(graph,0,sizeof(graph));
memset(earliest,0,sizeof(earliest));
memset(latest,0,sizeof(latest));
memset(inDegree,0,sizeof(inDegree));
memset(Time,0,sizeof(Time));
for(int i=1;i<=n;i++)
cin>>Time[i];
while(m--)
{
int from,to;
cin>>from>>to;
graph[from].push_back(to);
inDegree[to]++;
}
long long totalTime=CriticalPath(n);
long long answer=1;
for(int i=1;i<=n;i++)
{
answer*=latest[i]-earliest[i]+1;
}
cout<<totalTime<<endl<<answer%MOD<<endl;
}
return 0;
}