C++关键路径

前言

在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示该活动持续的时间,则这样的图称为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;
}

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值