目录
简介
拓扑排序是对有向无环图的顶点进行的一种线性排序,排序序列中每个顶点都会且仅会出现一次。对于所有有向边U→V,排完序之后,U都在V前面。
一个有向无环图可能存在多种排序结果
应用
- 先后顺序
- 判断图是否存在环
思考:
n个顶点的有向无环图,最多/最少存在几种合法的拓扑排序?
最多: n!种
最少: 1 种
流程
首先遍历整张图上的顶点,如果一个顶点的入度为0,将它加入S
当S不为空时:
在S中任取一个顶点x,将x加入到L的队尾,并把x从S中删去
遍历从x出发的边x→y,把这条边删掉,如果y的入度变成了0,则将其加入S中
循环结束时,如果所有点都加入了L,那么我们就找到了一个合法的拓扑排序,否则可以证明图中存在环。
时间复杂度
O(n + m)
使用priority_queue O(nlogn + m)
例题
图是否存在环
// problem :
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
std::vector<int> e[10005];
// in[i] i:結點的度數
int n, m, in[10005], q[10005];
inline void tuopuSort(){
int front = 1, rear = 0;
for(int i = 1; i <= n; ++i)
if(in[i] == 0)
q[++rear] = i;
while(front <= rear){
int x = q[front];
++front; // 出隊
for(auto y : e[x])
if(--in[y] == 0)
q[++rear] = y;
}
if(rear == n)
printf("No\n");
else
printf("Yes\n");
}
int main(){
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; ++i){
int x, y;
scanf("%d %d", &x, &y);
e[x].push_back(y);
++in[y];
}
tuopuSort();
return 0;
}
最小拓扑序列
// problem :
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
std::vector<int> e[10005];
// in[i] i:結點的入度
int n, m, in[10005], ans[10005], cnt;
priority_queue<int>q; // 默認大根堆
inline void TopoSort(){
for(int i = 1; i <= n; ++i)
if(in[i] == 0)
q.push(-i);
while(!q.empty()){
int x = -q.top();
q.pop();
ans[++cnt] = x;
for(auto y : e[x]){
if(--in[y] == 0)
q.push(-y);
}
}
for(int i = 1; i <= cnt; ++i)
printf("%d ", ans[i]);
}
int main(){
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; ++i){
int x, y;
scanf("%d %d", &x, &y);
e[x].push_back(y);
++in[y];
}
TopoSort();
return 0;
}
新年礼物
拓扑排序 + DP
从后往前DP
为什么从后往前, 了解一下拓扑序列的特征 对于所有U→V,排完序之后,U都在V前面
A B C D E 前面的小朋友依赖于后面的小朋友的礼物值
// problem :
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
int n, m, q[10005], in[10005], f[10005];
std::vector<int> e[10005];
void tuopuSort(){
int front = 1, rear = 0;
for(int i = 1; i <= n; ++i)
if(in[i] == 0)
q[++rear] = i;
while(front <= rear){
int x = q[front];
++front;
for(auto y : e[x]){
if(--in[y] == 0)
q[++rear] = y;
}
}
// 从后到前DP
for(int i = n; i >= 1; --i){
int x = q[i];
f[x] = 100;
for(auto y : e[x]){
f[x] = max(f[x], f[y] + 1);
}
}
ll ans = 0;
for(int i = 1; i <= n; ++i)
ans += f[i];
printf("%lld\n", ans);
}
int main(){
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; ++i){
int x, y; scanf("%d %d", &x, &y);
e[x].push_back(y);
++in[y];
}
tuopuSort();
return 0;
}