拓扑排序学习笔记
前言
今天教练让我们做这玩意的题,发现原来这玩意有这么多花样玩法
more and more vegetables,what should I do?
本来想看看认识的人有没有写这个的,看到zpl写的
感觉提高组图论全是拓扑排序。
于是,来填坑了。
希望能讲清楚,不清楚的地方请在下面留言。
由于作者太菜,因此文章通俗易懂;
前置芝士
拓扑排序是对有向无环图(DAG) 上的节点进行排序,
简单来说其实就是在不改变节点先后顺序的前提下把这一张图拉成一条链
拓扑排序最为经典的是Kahn算法
然而拓扑排序只在有向无环图(DAG) 上有结果;
下面我们来具体真实地模拟一下:
首先,先拿出所有入度为 的点,排放在最前面,
并且在原图中将他们删除;
我们以上面的图为例:
那么我们序列是这样增加的:
首先,最先取出的是 和
当然,最先取出的也可以是 和 ,这就证明了拓扑排序的不唯一性
之后,更新剩下的点的入度:
然后取出入度为 的 ,
最后取出 和
这时,序列就变成了:
来看一下这个过程,因为是有向无环图,而且删除的过程中不会出现不会产生环,那么就一直会存在入度为 点,珂以一直删除下去这道所有的点都被删没;
于是,可以给出下面一个复杂度为 的实现,这里 是点数, 为边数:(看没人用邻接表+队列,我来写一个吧)
int n, m;
vector<int>q[100005];
int r[100005];
queue<int>que;
void toposort() {
for (int i = 1; i <= n; i++) {
if (r[i] == 0) {
que.push(i);
}
}
while (que.empty()) {
int x = que.front();
que.pop();
cout << x << " ";
for (int i = 0; i < q[x].size(); i++) {
int y = q[x][i];
r[y]--;
if (r[y] == 0) {
que.push(y);
}
}
}
}
代码里面主要不用邻接矩阵的原因是这东西很浪费,
直接用vector
就可;
咋用这玩意
然后拓扑排序其实是可以简单的用来判环 的,在结尾加上返回值就可。
当然如果需要字典序最小就直接把queue
改成priority_queue
时间复杂度也会随之加上
我们来看几道应用,
可以算是一道很基础的板子题,
看到题面中说
(这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。)
我们其实就珂以确定这一定是拓扑排序了,最左端其实就是入度为 的点,按照刚刚提到的思路。
其实我觉得这道题我们不需要做,因为这个做法实在是让我不知道说啥,但是看在也是一道这个类型的,就加里头吧
拓扑排序很经典的就是最长路
显然,有最长路的只有 DAG
其实就是一个普通的排序再加上一个选择边的问题
那么选择边的问题可以用 dp 来解决
其实转移方程就是:
(注:其中, 代表的是入度为 的边, 就是对应的边)
总之就是很经典咧,可以看一下代码
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int main()
{
int n, m, r[1505], v[1505];
vector<int>g[1505];
vector<int>d[1505];
queue<int>q;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
g[a].push_back(b);
d[a].push_back(c);
r[b]++;
}
for (int i = 2; i <= n; i++)
{
v[i] = -1e9;
if (!r[i]) {
q.push(i);
}
}
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = 0; i < g[x].size(); i++) {
if (!--r[g[x][i]]) {
q.push(g[x][i]);
}
}
}
q.push(1);
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = 0; i < g[x].size(); i++)
{
if (v[g[x][i]] < v[x] + d[x][i]) {
v[g[x][i]] = v[x] + d[x][i];
}
if (!--r[g[x][i]]) q.push(g[x][i]);
}
}
if (v[n] == -1e9) cout << "-1";
else cout << v[n];
return 0;
}
下面这道题目还是很有意思的:
通过对相邻的两个名字比较,就可以得到 个关系,它们珂以构成一张图(暂且不管这到底有环没环),最后的字母表也满足这些关系。
如果存在环的话,可以用拓扑(见上);
如果不存在环的话,还可以用拓扑,拓扑排序就正解;
总结(?
在题目中其实就是要找到可以利用拓扑的地方(大部分DAG吧),然后再建图,知道题目具体要干啥,这样才不至于跑偏,最后输出。
总之吧,学好拓扑很有必要(
本文作者:zsdqwq
本文链接:https://www.cnblogs.com/wo-de-bo-ke-wo-zuo-zhu/p/toposort.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步