拓扑排序(\(Topological\) \(sorting\))

拓扑排序指的是有向无环图(\(DAG\));

学过计算机网络的知道计算机网络中有一个拓扑结构;

下面就是一个拓扑结构;

那拓扑序就是,图中任意一对顶点\(u\)和\(v\),若边\(<u,v>∈E(G)\),则\(u\)在线性序列中出现在\(v\)之前

我们可以发现 拓扑序不是唯一的

接下来,我们需要知道一个概念——

对于有向图的某个结点来说,我们把指向它的边的数量叫做入度

从它发出的边的数量称为出度,这个都很好理解吧;

拓扑排序 专题_结点

如何获得一个拓扑序:

我们先来找一个 最容易理解的例子,那就是食物链,对一个自然界的食物链来说,一定存在拓扑序;

第一:不存在环

第二:有向

接下来我们来看看如何获得一个拓扑序,我们观察最左面的结点,一定是没有指向它的边,也就是入度为零,最右边的结点呢,是一定不存在它指向别人的边的,如果有,那么它就不是最右边的点也就是出度为零;

那就是说,所有入度为零的点都可以作为起点,我们开一个数组记录入度的值;

至于如何使用邻接表存边,这就不展开解释了,可以参考这个:​​传送门​​

其实,拓扑排序也是\(bfs\)的一个简单应用,我们需要 借助队列 来实现;

首先,我们遍历存储入度的数组,获得可以作为起点的结点,将其加入队列;

接下来就可以愉快的遍历了,没当我们遍历到一个点的时候,我们让它的入度--;

这样做的 意义 就是,判断指向这个点的边是不是都遍历过了,因为我们要保证拓扑序最重要的一个特点:\(<u,v>\)的边中,\(u\)一定在\(v\)的前面出现;

如果这个点所有的边都遍历过的话,是不是也就是说这个点已经没有指向它的边了,也就是说这个点可以作为一个起点了,那我们将它加入队列;循环这个操作,知道队列为空;

​\(P4017\)​

最大食物链数量;最大指的是需要从一个入度为零的点开始到一个出度为零的点,这是一个完整的食物链,问我们给出的食物网中,食物链的数量
① 本题中,不仅需要 记录一下入度 , 还要 记录一下出度,这是因为我们要计算食物链的数量,食物链的最后一个结点,就是出度为零的点
② 食物链的数量,就是一个类似于 数字三角形 求值的\(dp\)问题了

#include <bits/stdc++.h>
using namespace std;

const int N = 5010, M = 500010;
const int INF = 0x3f3f3f3f, MOD = 80112002;

int in[N], out[N], f[N];
int n, m;

//链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void topsort() {
queue<int> q;

for (int i = 1; i <= n; i++)
if (!in[i]) {
q.push(i);
f[i] = 1;
}

while (q.size()) {
int u = q.front();
q.pop();
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
in[j]--;
f[j] = (f[j] + f[u]) % MOD;
if (in[j] == 0) q.push(j);
}
}
}

int main() {
scanf("%d%d", &n, &m);

memset(h, -1, sizeof h);

while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
in[b]++, out[a]++;
}

topsort();

int res = 0;

for (int i = 1; i <= n; i++)
if (!out[i]) res = (res + f[i]) % MOD;

printf("%d", res);
return 0;
}

​\(P1137\)​

这个题,我们根据题意是不是知道这个是一个\(DAG\),我们需要计算的是以城市 \(i\) 为终点最多能够游览多少个城市;这个是不是也是在一个拓扑序上做一个简单的\(dp\)就行了,我们记录一下最大值即可;

#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 2 * N, INF = 0x3f3f3f3f;

int n, m;

//链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

int in[N], f[N];

void topsort() {
queue<int> q;
for (int i = 1; i <= n; i++)
if (!in[i]) {
q.push(i);
f[i] = 1;
}

while (q.size()) {
int u = q.front();
q.pop();
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
f[j] = max(f[j], f[u] + 1);
in[j]--;
if (in[j] == 0) q.push(j);
}
}
}

int main() {
scanf("%d%d", &n, &m);

memset(h, -1, sizeof h);

for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
in[b]++;
}

topsort();

for (int i = 1; i <= n; i++) printf("%d\n", f[i]);

return 0;
}