[知识点] 8.4 拓扑排序与关键路径

总目录 > 8 图论 > 8.4 拓扑排序与关键路径

前言

想起当年参加 CTSC 时 DP 王 Zed 用拓扑序 DP 过了一道题直接一飞冲天,而一旁的蒟蒻拿了个 10 分 gg。

子目录列表

1、DAG 与 AOV 网

2、拓扑排序

3、AOE 网与关键路径

 

8.4 拓扑排序与关键路径

1、DAG 与 AOV 网

一个无环的有向图被称作有向无环图(DAG,Directed Acycline Graph)。它不同于有向树,DAG 将有向边改成无向边后可以出现环,而有向树不行。

DAG 应用广泛,除了在动态规划中提到了 4.4  树形 / DAG / 数位 DP ,它往往是描述一项工程或系统进行过程的有效工具。所有的大型工程(Project)都可以被划分为若干个子工程,这些子工程被称作活动(Activity)。在整个工程中,有些活动必须在其他相关活动完成后才能开始,也就是说,一个活动的开始是以其所有前序活动的完工为先决条件。部分活动没有先决条件,则可以随时开始。人们关心这个工程是否能够顺利进行,如果能,如何安排工程完成的先后顺序使得所花费时间最短,而 DAG 正是形象反映出整个工程的活动之间的约束关系的最佳选择。

顶点表示活动有向边表示活动之间的约束关系,这样的 DAG 被称作顶点活动网(AOV 网,Activity On Vertex Network)

 

2、拓扑排序

① 定义

根据上述对 AOV 网的定义,我们先来举个例子。

杰克对计算机很感兴趣,进入大学后他如愿以偿地进修计科专业。学校发给他一份培养方案,上面写满了各种课程:

课程代号  课程名称            先修课程
C1            高等数学            无
C2            C 程序设计        无
C3            离散数学            C1, C2
C4            数据结构            C3, C5
C5            C++ 程序设计    C2
C6            编译技术            C4, C5
C7            操作系统            C4, C9
C8            大学物理            C1
C9            计算机组成        C8

杰克表示头都晕了,他想理清楚这些课程的关系。这时候,使用一张 AOV 网来展示:

一下子就变得清晰起来 —— 他需要先上 C1 高等数学 和 C2 C语言设计 两门课程,而后 C3 离散数学,C5 C++程序设计,C8 大学物理 就可以上了;以此类推,最后上 C7 操作系统,C6 编译技术。

AOV 网为什么是 DAG 图?如果出现了环,对于课程的选择则必然出现问题:假设 C1 是 C8 的前序课程,而 C8 又是 C1 的前序课程,那么两门课上课的先后顺序则无法确定,导致整个课程学习无法完成。这种情况如果出现在工程中,则称为死锁死循环,是必须要避免的。

杰克整理好了他的上课顺序,为:

{C1, C2, C3, C8, C5, C4, C9, C7, C6}

根据 AOV 网求得的这样的序列,我们称之为拓扑序(Topological order)。构造拓扑序的这个过程,称之为拓扑排序(Topological sort)

② 性质

注意到,比如刚开学时,杰克可以选择先上 C1 再上 C2,反之同样可以;学完了 C1 和 C2,杰克可以先上 C3 也可以先上 C8……也就是说,拓扑序列并不具有唯一性

通过归纳证明,容易得到如下推理:

图能进行拓扑排序的充分必要条件是该图为 DAG。两者为等价关系。判定一个图是否为 DAG,检验它能否进行拓扑排序即可。

③ Kahn 算法

Kahn 算法用于实现拓扑排序。预处理出所有结点的入度,将入度为 0 的结点加入集合 S,每次从 S 中取出任意一个顶点 u 并保存到数组 ans,遍历以 u 为起点的所有边,并将对应终点的入度减 1,如果减到了 0,则加入集合 S,不断重复,直到所有顶点输出完毕。ans 数组最终的结果即拓扑序。

Kahn 算法使用队列实现。

④ 例题与代码

AOV 网在生活中还是很常见的,课程之间的关系,组件之间的依赖关系,工程的先后顺序等等。下面还给出一道例题:

【例题】【士兵排队】

有 n 名士兵(1 <= n <= 100),编号依次为 1, 2, 3, ..., n。进行队列训练时,指挥官要把一些士兵从高到矮依次排成一行,但现在指挥官不能直接获得每个士兵的身高信息,只能获得“P1 比 P2 高”这样的比较结果(P1, P2 ∈ {1, 2, ..., n},记为 P1 > P2),如“A > B”表示 A 比 B 高。编一程序,根据所得到的比较结果求出符合条件的排队方案。注:比较结果中没有涉及到的士兵不参加排队。

例如,设有3个士兵 1, 2, 3,给出关系 (1, 2), (2, 3)。其中 (1, 2) 表示士兵 1 高于 2,当上面的关系给出之后,可以将他们排成一队:123。

输入:第一行两个正整数 n, m,接下来 m 行中每行两个整数 x, y,表示士兵 x 比士兵 y 高。

输出:从高到矮依次输出每一个士兵的编号,如果没有答案输出 -1。

 

根据身高关系直接构建 AOV 网,使用 Kahn 算法求出拓扑序,即最终答案。

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 105
 5 #define MAXM 205
 6 
 7 int n, m, u, v, a[MAXN], tot, h[MAXN], q[MAXN];
 8 
 9 class Edge {
10 public:
11     int v, nxt;
12 } e[MAXM];
13 
14 void add(int u, int v) {
15     tot++, e[tot] = (Edge) {v, h[u]}, h[u] = tot;
16     a[v]++;
17 }
18 
19 void Kahn() {
20     for (int i = 1; i <= n; i++) 
21         if (!a[i]) q[tail++] = i;
22     int head = 1, tail = 1;
23     while (head != tail) {
24         int o = q[head];
25         for (int x = h[o]; x; x = e[x].nxt) 
26             if (!--a[e[x].v]) q[tail++] = e[x].v;
27         head++;
28     }
29 }
30 
31 int main() {
32     cin >> n >> m;
33     for (int i = 1; i <= m; i++) cin >> u >> v, add(u, v);
34     for (int i = 1; i <= n; i++) cout << q[i] << ' ';
35     return 0;
36 }

 

3、AOE 网与关键路径

① AOE 网

与 AOV 网相对应的是 AOE 网(Activity On Edge Network),即边表示活动的网。AOE 网是赋权 DAG 图,顶点表示事件(Event),弧表示活动,权表示活动持续的时间。通常,AOE 网是用来估算工程的完成时间

与 AOV 网不同的是,AOE 网必然只有一个入度为 0 的点(起点)一个出度为 0 的点(终点)。从起点出发,各个活动可以并行进行,所以完成工程的最短时间是从起点到终点的最长路径的长度。这条最长的路径称之为关键路径

② 关键路径

假设开始点为 u,从 u 到结点 vi 的最长路径长度叫做事件 vi 的最早发生时间。这个时间决定了所有以事件 vi 为标志而可以开始进行的活动的最早发生时间。对于活动 ai,其最早发生时间用 ei 表示。在不推迟整个工程完成进度的前提,即不出现没有活动正在进行的情况,活动 ai 的最迟发生时间 li 表示。li 和 ei 的差值为活动 ai 的时间余量。时间余量为 0 的活动被称作关键活动关键路径上的所有活动均为关键活动。

也就是说,非关键活动的进度的提前与延后(在 [ei, li] 内),其实对整个工程的进度是没有影响的。所以,分析这种工程的关键在于找到关键活动,即找到 ei = li 的 ai,以提高工效,缩短工期。

③ 实现

在 Kahn 算法求出拓扑序的基础上,增加一个拓扑序结点栈,按照拓扑序搜索各个活动,及计算相应事件的最早 / 最迟开始时间。具体过程暂略。

posted @ 2020-06-06 23:43  jinkun113  阅读(513)  评论(0编辑  收藏  举报