AOE-网(拓扑排序的集成应用)

情景引入:

在生活中,我们会精细规划路线或者安排日程;在工程项目中,我们会细心安排施工流程;在工作中,我们会有先后作业以提高效率......这些场景展示了AOE网在不同领域的应用,它的核心价值在于通过图形化的方式帮助理解和优化复杂系统中的活动顺序和时间管理。

核心解读:

1,AOE-网是一个带权的有向无环图,其中顶点表示事件,弧表示活动,权表示活动持续的时间

2,“路径长度”最长的路径叫关键路径

为了方便求解,我们进行一些假设:

1,活动记为a1,a2,a3......

2,   事件记为v1,v2,v3,v4......

3,活动最早开始时间记为e(i)--a(i)

4,活动最迟开始时间记为L(i)--a(i)

5,满足L(i)=e(i)的活动称为关键活动,想清楚关键路径上的活动全是关键活动!否则会有时间余量

6,事件最早发生时间为Se(j)--v(j)

7,事件最迟发生时间为SL(j)--v(j)

基本思路:求关键路径

(1)输入e条弧{j, k), 建立AOE-网的存储结构;建立AOE网的存储结构,通常使用邻接表来表示

(2)从源点v0出发,令Se[0]=0, 按拓扑有序求其余各顶点的最早发生时间Se[i](1⩽i⩽n-1)。如果得到的拓扑有序序列中顶点个数小于网中顶点数n, 则说明网中存在环,不能求关键路径,算法终止;否则执行步骤

(3)从汇点vn出发,令SL[n-1]=Se[n-1], 按逆拓扑有序求其余各顶点的最迟发生时间SL[i](n-2⩾i⩾2)

(4)根据各顶点的Se和SL值,求每条弧s的最早开始时间e(s)和最迟开始时间L(s)若某条弧满足条件e(s)=L(s), 则为关键活动。

(5),将所有关键活动连接起来,形成关键路径,输出关键路径,这代表了项目的最短完成时间。

代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>



#define MAX_VERTICES 100
#define INF 999999

int n; // 顶点数
int e; // 边数
int Se[MAX_VERTICES]; // 最早发生时间
int SL[MAX_VERTICES]; // 最迟发生时间
int graph[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图

// 拓扑排序,计算最早发生时间
int topologicalSort() {
    int indegree[MAX_VERTICES] = { 0 };
    int stack[MAX_VERTICES], top = -1;
    int i, j, k;

    // 计算入度
    for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
            if (graph[i][j] != 0) {
                indegree[j]++;
            }
        }
    }

    // 将所有入度为0的顶点入栈
    for (i = 0; i < n; i++) {
        if (indegree[i] == 0) {
            stack[++top] = i;
        }
    }

    int count = 0;
    while (top != -1) {
        int u = stack[top--];
        Se[u] = (u == 0) ? 0 : Se[u - 1] + graph[u - 1][u]; // 源点的最早发生时间为0
        for (i = 0; i < n; i++) {
            if (graph[u][i] != 0) {
                Se[i] = (Se[i] > Se[u] + graph[u][i]) ? Se[i] : Se[u] + graph[u][i];
                indegree[i]--;
                if (indegree[i] == 0) {
                    stack[++top] = i;
                }
            }
        }
        count++;
    }

    if (count != n) {
        printf("有环\n");
        return 0;
    }
    return 1;
}

// 逆拓扑排序,计算最迟发生时间
void inverseTopologicalSort() {
    int i, j, k;
    for (i = n - 1; i >= 0; i--) {
        for (j = n - 1; j >= 0; j--) {
            if (graph[j][i] != 0) {
                SL[i] = (SL[i] < SL[j] - graph[j][i]) ? SL[i] : SL[j] - graph[j][i];
            }
        }
        SL[i] = (i == n - 1) ? Se[n - 1] : SL[i];
    }
}

配图理解:

下次再见,最近二个月一直在gitee,望谅解!

### ✅ 什么是 AOE-的关键路径? **关键路径(Critical Path)** 是 **AOE-(Activity on Edge Network)** 中从源点到汇点的**最长路径**,它决定了完成整个工程所需的最短时间。关键路径上的活动称为**关键活动**,它们一旦延迟,整个工程就会延期。 --- ## 📌 一、基本概念 | 名称 | 含义 | |------|------| | **AOE-** | Activity On Edge network,表示在边上的活动络。用于建模工程进度。 | | **顶点** | 表示事件(Event),是某些活动的开始或结束。 | | **有向边** | 表示活动(Activity),边上权值表示活动持续时间。 | | **源点** | 唯一入度为0的顶点,表示工程的开始。 | | **汇点** | 唯一出度为0的顶点,表示工程的结束。 | | **关键路径** | 从源点到汇点的最长路径(按活动时间总和计算)。 | > ⚠️ 注意:虽然“最短工期”听起来像是要找最短路径,但由于所有活动必须完成,所以关键路径是**最长路径**! --- ## 🔍 二、关键路径的意义 - 决定整个工程的**最短完成时间** - 找出哪些活动是**不能拖延的**(即关键活动) - 帮助优化资源分配:非关键活动可以适当延后 --- ## ✏️ 三、求解关键路径的步骤(算法思想) 我们通过以下四个量来找出关键路径: | 符号 | 名称 | 定义 | |------|------|------| | `ve[i]` | 事件 `i` 的最早发生时间 | 所有进入该事件的活动都完成后,事件才能发生 | | `vl[i]` | 事件 `i` 的最迟发生时间 | 在不推迟整个工程的前提下,事件最晚可发生的时刻 | | `e(i)` | 活动 `i` 的最早开始时间 | = 弧尾事件的 `ve` | | `l(i)` | 活动 `i` 的最迟开始时间 | = 弧头事件的 `vl - 活动持续时间` | 如果某个活动满足:`e(i) == l(i)`,则它是**关键活动** 所有关键活动连接起来就构成了**关键路径** --- ## 🧩 四、算法步骤详解 1. **拓扑排序**:计算每个事件的 `ve[]` - 初始化:`ve[0..n-1] = 0` -拓扑顺序遍历每个顶点 - 对于每条出边 `<i, j>`,权值为 `w`: ``` ve[j] = max(ve[j], ve[i] + w) ``` 2. **逆拓扑排序**:计算每个事件的 `vl[]` - 初始化:`vl[汇点] = ve[汇点]` - 其余初始化为 `vl[i] = 最大值` → 实际上设为 `vl[i] = vl[汇点]` - 按逆拓扑序处理每个顶点 - 对于每条出边 `<i, j>`,权值为 `w`: ``` vl[i] = min(vl[i], vl[j] - w) ``` 3. 计算每个活动的: - `e(i) = ve[弧尾]` - `l(i) = vl[弧头] - weight` 4. 若 `e(i) == l(i)`,则该活动是关键活动 5. 所有关键活动组成的路径就是关键路径 --- ## 💡 五、Python 示例代码实现 ```python from collections import deque, defaultdict class Graph: def __init__(self, vertices): self.V = vertices self.graph = defaultdict(list) # 邻接表: graph[u] = [(v, w), ...] self.in_degree = [0] * vertices def add_edge(self, u, v, w): self.graph[u].append((v, w)) self.in_degree[v] += 1 def topological_sort(self): ve = [0] * self.V queue = deque() topo_order = [] # 找入度为0的起点 for i in range(self.V): if self.in_degree[i] == 0: queue.append(i) while queue: u = queue.popleft() topo_order.append(u) for v, w in self.graph[u]: ve[v] = max(ve[v], ve[u] + w) self.in_degree[v] -= 1 if self.in_degree[v] == 0: queue.append(v) return ve, topo_order def critical_path(self): # 步骤1: 拓扑排序,得到 ve 和 topo_order ve, topo_order = self.topological_sort() if len(topo_order) != self.V: print("存在环,无法计算关键路径") return # 步骤2: 初始化 vl 数组 vl = [ve[-1]] * self.V # 假设最后一个事件是汇点 # 步骤3: 逆拓扑序更新 vl for i in reversed(topo_order): for j, w in self.graph[i]: vl[i] = min(vl[i], vl[j] - w) # 输出结果 & 查找关键路径 print("事件\t ve\t vl") for i in range(self.V): print(f"{i}\t {ve[i]}\t {vl[i]}") print("\n活动 (u->v)\t最早开始(e)\t最迟开始(l)\t是否关键") critical_edges = [] for u in range(self.V): for v, w in self.graph[u]: e = ve[u] # 活动最早开始时间 l = vl[v] - w # 活动最迟开始时间 is_critical = '是' if e == l else '否' print(f"{u}->{v}({w})\t\t{e}\t\t{l}\t\t{is_critical}") if e == l: critical_edges.append((u, v, w)) print("\n关键路径上的活动:", critical_edges) return critical_edges # 示例使用 g = Graph(6) g.add_edge(0, 1, 3) g.add_edge(0, 2, 2) g.add_edge(1, 3, 4) g.add_edge(2, 3, 5) g.add_edge(3, 4, 6) g.add_edge(4, 5, 2) print("AOE关键路径分析:") g.critical_path() ``` --- ## 🎯 输出示例解释: 假设输入如下图结构: ``` (3) (4) (6) (2) 0 ------> 1 -----> 3 -----> 4 -----> 5 \ \ \ (2) \(5) \ \ ——> 2 ————> 3 ``` 运行后会发现: - 关键路径可能是:0 → 2 → 3 → 4 → 5,总长度 = 2+5+6+2 = 15 - 或者 0 → 1 → 3 → 4 → 5 = 3+4+6+2 = 15(并列) 哪个是关键路径取决于具体数据。 --- ## ✅ 总结 | 项目 | 内容 | |------|------| | 关键路径定义 | AOE中从源点到汇点的最长路径 | | 关键活动判断条件 | `e(i) == l(i)` | | 工程最短工期 | = `ve[汇点]` 或 `vl[汇点]` | | 关键路径特点 | 路径上所有活动都是关键活动,无冗余时间 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值