数据结构之关键路径

1、AOE网

AOE网是⼀个带权的有向无环图,其中顶点表示事件(Event),弧表示活动,权表示活动持续的时间。

2、源点和汇点

AOE网中入度为零的点称为源点,将出度为零的点称为汇点。

3、关键路径

完成工程的最短时间是从源点到汇点的最长路径的长度。路径长度最长的路径就叫做关键路径。

ETV(Earliest Time Of Vertex):事件最早发生时间,就是顶点的最早发生时间。LTV(Latest Time Of Vertex):事件最晚发生时间,就是每个顶点对应的事件最晚需要开始的时间,如果超出此时间将会延误整个工期。ETE(Earliest Time Of Edge):活动的最早开工时间,就是弧的最早发⽣时间。LTE(Lastest Time of Edge):活动的最晚开工时间,就是不推迟工期的最晚开工时间。

4、关键路径算法

关键路径(Critical Path)针对有向无环加权图(AOE网,工程活动网络),是从源点(入度为0)到汇点(出度为0)的最长路径。路径上的活动称为关键活动,关键路径长度决定整个工程的最短完成时间,关键活动延时会直接导致整体工程延时。

步骤1:拓扑排序

对AOE网进行拓扑排序,得到有序节点序列,若图存在环则无关键路径,工程无法正常进行。

步骤2:计算最早发生时间 ve\[\]

vei:节点i(事件)的最早发生时间,从源点正向推导。

公式:源点 vestart = 0;遍历拓扑序列,对当前节点u的所有出边 u→v(权重w),执行 vev = max(vev, veu + w)。

步骤3:计算最晚发生时间 vl\[\]

vli:节点i(事件)的最晚发生时间,不推迟整体工程工期,从汇点逆向推导。

公式:汇点 vlend = veend;逆序遍历拓扑序列,对当前节点v的所有入边 u→v(权重w),执行 vlu = min(vlu, vlv - w)。

步骤4:筛选关键节点与关键路径

若节点满足 vei == vli,该节点为关键节点;由相邻关键节点组成的路径,即为关键路径。

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 105;
const int INF = 0x3f3f3f3f;

// 邻接表存储: to目标节点, weight边权, next下一条边
struct Edge {
    int to, w;
    Edge(int t, int ww) : to(t), w(ww) {}
};
vector<Edge> adj[MAXN];   // 正向邻接表(拓扑、求ve)
vector<Edge> reverseAdj[MAXN]; // 逆向邻接表(求vl)
int inDegree[MAXN];      // 入度数组
vector<int> topoSeq;     // 存储拓扑排序序列
int ve[MAXN], vl[MAXN];  // 最早、最晚发生时间

// 拓扑排序
bool topoSort(int n) {
    queue<int> q;
    // 初始化入度为0的节点入队
    for (int i = 1; i <= n; i++) {
        if (inDegree[i] == 0)
            q.push(i);
    }

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        topoSeq.push_back(u);
        // 遍历出边,更新后继节点入度
        for (Edge e : adj[u]) {
            int v = e.to;
            inDegree[v]--;
            if (inDegree[v] == 0)
                q.push(v);
        }
    }
    // 拓扑序列长度不等于节点数,说明有环,无关键路径
    return topoSeq.size() == n;
}

// 计算关键路径
void criticalPath(int n) {
    // 1. 初始化ve数组,正向求最早发生时间
    memset(ve, 0, sizeof(ve));
    for (int u : topoSeq) {
        for (Edge e : adj[u]) {
            int v = e.to, w = e.w;
            if (ve[v] < ve[u] + w) {
                ve[v] = ve[u] + w;
            }
        }
    }

    // 2. 初始化vl数组,逆向求最晚发生时间
    int endNode = topoSeq.back();
    fill(vl, vl + n + 1, INF);
    vl[endNode] = ve[endNode];
    // 逆序遍历拓扑序列
    for (int i = topoSeq.size() - 1; i >= 0; i--) {
        int v = topoSeq[i];
        for (Edge e : reverseAdj[v]) {
            int u = e.to, w = e.w;
            if (vl[u] > vl[v] - w) {
                vl[u] = vl[v] - w;
            }
        }
    }

    // 3. 输出关键节点和关键路径
    cout << "===== 关键路径计算结果 =====" << endl;
    cout << "工程最短总工期:" << ve[endNode] << endl;
    cout << "关键节点:";
    for (int i = 1; i <= n; i++) {
        if (ve[i] == vl[i]) {
            cout << i << " ";
        }
    }
    cout << endl;

    cout << "关键活动(边):" << endl;
    for (int u = 1; u <= n; u++) {
        // 筛选关键节点之间的边
        if (ve[u] != vl[u]) continue;
        for (Edge e : adj[u]) {
            int v = e.to, w = e.w;
            if (ve[v] == vl[v] && ve[v] == ve[u] + w) {
                cout << "节点" << u << " -> 节点" << v << " (工期:" << w << ")" << endl;
            }
        }
    }
}

int main() {
    // 初始化
    memset(inDegree, 0, sizeof(inDegree));
    int n, m;
    cout << "请输入节点数、边数:";
    cin >> n >> m;

    // 构建正向、逆向邻接表
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cout << "请输入第" << i + 1 << "条边(起点 终点 权重):";
        cin >> u >> v >> w;
        adj[u].emplace_back(v, w);
        reverseAdj[v].emplace_back(u, w);
        inDegree[v]++;
    }

    // 拓扑排序判环
    if (!topoSort(n)) {
        cout << "图中存在环路,无关键路径!" << endl;
        return 0;
    }

    // 计算并输出关键路径
    criticalPath(n);
    return 0;
}
相关推荐
阿宇的技术日志18 小时前
漏桶、令牌桶、滑动窗口 三限流算法理解
算法·滑动窗口·漏桶·令牌桶
来一碗刘肉面18 小时前
队列的链式实现
数据结构·c++·算法·链表
KaMeidebaby19 小时前
卡梅德生物技术快报|原核膜蛋白表达优化实操手册,膜蛋白的纯化梯度洗脱完整流程
前端·网络·数据库·人工智能·算法
naturerun19 小时前
逐步插入回路法构造欧拉回路的算法
c++·算法
qizayaoshuap19 小时前
# [特殊字符] 骰子模拟器 — 鸿蒙ArkTS随机算法与动画系统设计
算法·华为·harmonyos
山顶夕景19 小时前
【DWT】计算两不等序列相似度:DWT
算法·动态规划·检索·模式识别·相似度
奶人五毛拉人一块20 小时前
二分算法以及习题讲解
数据结构·算法·二分
兰令水20 小时前
hot100【acm版】【2026.7.19打卡-java版本】
java·数据结构·算法·leetcode·面试
Zachery Pole20 小时前
CCF-CSP备战NO.6【栈】
算法·深度优先