拓撲排序 專題
拓撲排序(\(Topological\) \(sorting\))
拓撲排序指的是有向無環圖(\(DAG\));
學過計算機網路的知道計算機網路中有一個拓撲結構;
下面就是一個拓撲結構;
那拓撲序就是,圖中任意一對頂點\(u\)和\(v\),若邊\(<u,v>∈E(G)\),則\(u\)線上性序列中出現在\(v\)之前
我們可以發現 拓撲序不是唯一的;
接下來,我們需要知道一個概念——度:
對於有向圖的某個結點來說,我們把指向它的邊的數量叫做入度;
把從它發出的邊的數量稱為出度,這個都很好理解吧;
如何獲得一個拓撲序:
我們先來找一個 最容易理解的例子,那就是食物鏈,對一個自然界的食物鏈來說,一定存在拓撲序;
第一:不存在環;
第二:有向;
接下來我們來看看如何獲得一個拓撲序,我們觀察最左面的結點,一定是沒有指向它的邊,也就是入度為零,最右邊的結點呢,是一定不存在它指向別人的邊的,如果有,那麼它就不是最右邊的點也就是出度為零;
那就是說,所有入度為零的點都可以作為起點,我們開一個數組記錄入度的值;
至於如何使用鄰接表存邊,這就不展開解釋了,可以參考這個:傳送門
其實,拓撲排序也是\(bfs\)的一個簡單應用,我們需要 藉助佇列 來實現;
首先,我們遍歷儲存入度的陣列,獲得可以作為起點的結點,將其加入佇列;
接下來就可以愉快的遍歷了,沒當我們遍歷到一個點的時候,我們讓它的入度--;
這樣做的 意義 就是,判斷指向這個點的邊是不是都遍歷過了,因為我們要保證拓撲序最重要的一個特點:\(<u,v>\)
如果這個點所有的邊都遍歷過的話,是不是也就是說這個點已經沒有指向它的邊了,也就是說這個點可以作為一個起點了,那我們將它加入佇列;迴圈這個操作,知道佇列為空;
最大食物鏈數量;最大指的是需要從一個入度為零的點開始到一個出度為零的點,這是一個完整的食物鏈,問我們給出的食物網中,食物鏈的數量
① 本題中,不僅需要 記錄一下入度 , 還要 記錄一下出度,這是因為我們要計算食物鏈的數量,食物鏈的最後一個結點,就是出度為零的點
② 食物鏈的數量,就是一個類似於 數字三角形 求值的\(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; }
這個題,我們根據題意是不是知道這個是一個\(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;
}