筆記:拓撲排序
定義
拓撲排序(Topological sorting),是對一個 DAG 排序的演算法。
對於排序後的序列 \(s\),設 \(t_i\) 是節點 \(i\) 在 \(s\) 中的位置,那麼對於該 DAG 上的每條邊 \(u\to v\) 有 \(t_u<t_v\)。
換句話說,就是每條邊 \(u\to v\),\(u\) 不能在 \(v\) 的後面。
拓撲排序得到的序列 \(s\) 可能有多種結果。
目前關於任意 DAG 的所有拓撲序數量問題還沒有多項式做法(無特殊性質),但是可以考慮狀態壓縮 dp。
link。
做法
考慮兩種演算法,分別基於廣搜(BFS)和深搜(DFS)。
BFS
使用佇列來維護。
由於要取最前面的,也就是初始化將所有入度為 \(0\) 的結點入隊。
取出隊頭 \(u\),刪掉以 \(u\) 為起點的所有邊 \(u\to v\)。
如果刪邊過程中某些結點 \(v\) 入度變為 \(0\),那麼將 \(v\) 入隊。
重複以上操作,直到隊空。
DFS
使用棧(遞迴)來維護。
考慮一個結點 \(u\),刪去以 \(u\) 為起點的所有邊 \(u\to v\)。
如果在此過程中某個節點 \(v\) 的入度變為 \(0\),對 \(v\) 進行 DFS。
初始化要對所有入度為 \(0\) 的結點進行 DFS。
模板
link。
時間複雜度 \(\mathcal{O}(n+m)\)
程式碼(BFS)。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,x,in[N];
vector<int> g[N];
void topo(){
queue<int> q;
fr1(1,n,1) if(in[i]==0) q.push(i);
while(!q.empty()){
int u=q.front();
q.pop();
cout<<u<<" ";
for(auto i:g[u]) if(--in[i]==0) q.push(i);
}
}
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
while(x) g[i].push_back(x),in[x]++,cin>>x;
}
topo();
return 0;
}
程式碼(DFS)。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,x,in[N];
bool vis[N];
vector<int> g[N];
void topo(int u){
if(vis[u]==0) cout<<u<<" ",vis[u]=1;
for(auto i:g[u]) if(--in[i]==0) topo(i);
}
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
while(x) g[i].push_back(x),in[x]++,cin>>x;
}
for(int i=1;i<=n;i++) if(in[i]==0) topo(i);
return 0;
}
其他
拓撲排序還能解決其他問題:
- 拓撲序+dp
- 最小(大)字典序拓撲序
等等。