关键路径 相关题目

基础知识点

顶点活动(AOV)网是指用顶点表示活动,而用边集表示活动间优先关系的有向图。显然,图中不应当存在有向环,否则会让优先关系出现逻辑错误。

边活动(AOE)网是指用带权的边集表示活动,而用顶点表示事件的有向图,其中边权表示完成活动需要的时间。一般来说,AOE网用来表示一个工程的进行过程,而工程常常可以分为若干子工程(即“活动”),显然AOE网不应当有环,否则会出现和AOV网一样的逻辑问题。AOE网是基于工程提出的概念,主要着重解决两个问题:

  1. 工程起始到终止至少需要多少时间
  2. 哪些路径上的活动是影响整个工程进度的关键。

如果给定AOV网中各项顶点活动所需要的时间,那么可以将AOV网转换为AOE网。比较简单的方法是:将AOV网中每个顶点都拆分为两个顶点,分别表示活动的起点和终点,而两个顶点之间用有向边连接,该有向边表示原顶点的活动,边权给定;原AOV网中的边全部视为空活动,边权为0。

把入度为0的顶点叫做源点,出度为0的顶点叫做汇点。

一个AOE网络至少包含一个源点和一个汇点。

关键路径:从源点到汇点的路径长度最大的路径。

  • 对于最早开始时间的初始化,如果题目没有特殊要求,那么所有结点都初始化为0。
  • 对于最晚开始时间的初始化,如果题目没有特殊要求,那么汇点的最晚开始时间初始化为它的最早开始时间,其余结点初始化为INF。

求最长路径的方法

方法一

对于一个没有正环的图(指源点可达的正环,下同),如果需要求最长路径长度,则可以把所有边的边权乘以-1,令其变为相反数,然后使用Bellman-Ford算法或者SPFA算法求最短路径长度,将所得结果取反即可。

方法二

适用条件:给定一个有向无环图,求解整个图的所有路径中权值之和最大的那条。

采用动态规划+递归的方法,令dp[i]表示从i号顶点出发能获得的最长路径长度。

dp[i] = max{dp[j]+length[i-->j]},其中j是i的后继结点

// dp[i]表示从i号顶点出发能获得的最长路径长度,  初始化为0
int DP(int i){
    if(dp[i]>0) return dp[i];  // dp[i]已计算得到
    for(int j=0;j<n;j++){  // 遍历结点i的所有出边
        if(G[i][j]!=INF){
            dp[i] = max(dp[i],DP(j)+G[i][j]);  // 用DP(j)的方式获取dp[j], 借助递归策略实现拓扑逆序的同等效果
        }
    }
    return dp[i];  // 返回计算完毕的dp[i]
}

POJ4109

题目大意:重新排列指令,以便CPU可以使用最短的时间完成所有指令

分析

        若将指令的先后依赖关系抽象为有向边,则这个实际问题就转换为判断图上最长路径的长度,即关键路径的长度。可以根据拓扑序列逐一求出每个活动的最早开始时间,再根据拓扑序列的逆序列求出每个活动的最晚开始时间。所有活动中最早开始时间和最晚开始时间相同的活动为关键活动,而所有活动中最早开始时间的最大值便是关键路径的长度。

        值得注意的是,题目中指出每个指令的执行时间 为1ns,也就是说,所有源点的最早开始时间应该初始化为1,而非通常的0。

代码

#include <iostream>
#include <cstring>
#include <climits>
#include <queue>
#include <vector>
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> myQueue;
    for(int i=0;i<n;i++){
        if(inDegree[i]==0){
            myQueue.push(i);
            earliest[i] = 1;   // 初始化为1
        }
    }
    while(!myQueue.empty()){
        int u = myQueue.front();
        topology.push_back(u);
        myQueue.pop();
        for(int i=0;i<graph[u].size();i++){
            int v = graph[u][i].to;
            int length = graph[u][i].length;
            earliest[v] = max(earliest[v], earliest[u]+length);
            inDegree[v]--;
            if(inDegree[v]==0) myQueue.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 dd = graph[u][j].length;
            latest[u] = min(latest[u],latest[v]-dd);
        }
    }
}
int main(){
    int n,m;
    while(cin>>n>>m){
        memset(graph,0,sizeof(graph));
        for(int i=0;i<n;i++){
            earliest[i]=latest[i]=inDegree[i]=0;
        }
        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;
}

论文(清华大学复试上机题)

代码

#include <iostream>
#include <cstring>
#include <climits>
#include <queue>
#include <vector>
using namespace std;
const int MAXN=1e5+1;
const int INF = INT_MAX;
const int MOD = 1e9+7;  // 这里没加
struct Edge {
    int to;
    long long length;
    Edge(int t,long long len):to(t),length(len) {}
};
vector<Edge> graph[MAXN];
long long inDegree[MAXN],earliest[MAXN],latest[MAXN],a[MAXN];
long long CriticalPath(int n) {
    vector<int> topology;
    queue<int> myQueue;
    for(int i=1; i<=n; i++) {
        if(inDegree[i]==0) {
            myQueue.push(i);
        }
    }
    while(!myQueue.empty()) {
        int u=myQueue.front();
        myQueue.pop();
        topology.push_back(u);
        for(int i=0; i<graph[u].size(); i++) {
            int v = graph[u][i].to;
            long long len = graph[u][i].length;
            inDegree[v]--;
            if(inDegree[v]==0) {
                myQueue.push(v);
            }
            earliest[v] = max(earliest[v],earliest[u]+len);
        }
    }
    long long totalTime=0;
    for(int i=1; i<=n; i++) {
        totalTime = max(totalTime,earliest[i]+a[i]);
    }
    for(int i=topology.size()-1; i>=0; i--) {
        int u=topology[i];
        if(graph[u].size()==0)
            latest[u] = totalTime - a[u];
        else
            latest[u] = INF;
        for(int j=0; j<graph[u].size(); j++) {
            int v = graph[u][j].to;
            long long len = graph[u][j].length;
            latest[u] = min(latest[u],latest[v]-len);
        }
    }
    return totalTime;
}
int main() {
    int N,M;
    while(cin>>N>>M) {
        memset(graph,0,sizeof(graph));
        for(int i=1; i<=N; i++) { // 编号是1~N,而不是0~N-1
            inDegree[i] = latest[i] = earliest[i]=0;
            cin>>a[i];
        }
        for(int i=0; i<M; i++) {
            int from,to;
            cin>>from>>to;
            graph[from].push_back(Edge(to,a[from]));
            inDegree[to]++;
        }
        cout<<CriticalPath(N)<<endl;
        long long ans = 1;
        for(int i=1; i<=N; i++){
            ans *= (latest[i] - earliest[i] + 1);
            ans %= MOD;
        }
        cout<<ans<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值