(鬼 知 道 刚 学 这 破 玩 意 的 时 候 我 有 多 绝 望,现在再回来学,嗯! 真 可 爱 )
基本定义
拓扑序有以下几个特点
- 拓扑序中的点没有指向它前面点的边(拓扑序中我们讨论的边均为有向边)
- 由上一条可得拓扑序中若A指向B,则A一定排在B的前面
- 要讨论拓扑序则该图中不得有环
- 每个顶点在一个拓扑序中只出现一次
- 一个图的正确拓扑序不止有一种
算法思想
每个点都有入度和出度,当某点入度为0时,则没有指向它的边,由拓扑序的第二条特点得出,这时没有点必须排在它的前面,于是将它加入拓扑序,就相当于在原图中删掉这个点,该点指向的所有点入度减一,重复此过程,直到所有点都加入了拓扑序
手动模拟
(图源见水印)
敲完算法思想之后我自己都晕咳咳咳,我们来手动模拟一下上面那个图(我 最 喜 欢 手 动 模 拟 数 据 了 !)
初始图就是(a),图中入度为0的点是 1,6,所以1,6入队,这时的队(拓扑序)为{6,1}(顺序无所谓,我是为了配合图)
从队首开始,删掉队首点,将队首连出的边都删掉,相连的点入度-1,于是就得到了(b),6出队,此时的队列为{1}
重复上一步,最后得到出队的顺序就是拓扑序
算法实现
我在我平时用的几个oj上,我实在是没找到纯裸题QwQ,就直接自己敲了一个裸代码
#include<bits/stdc++.h>
using namespace std;
int rd[10100],cd[10100],n,m,p[10100][10100],q[1010];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
rd[b]++;//点b的入度++
cd[a]++;//点a的出度++
p[a][cd[a]]=b;//从点a出发的第cd[a]点是b
}
int l=1;
int r=0;//队列的左右端点
for (int i=1;i<=n;i++)
if (rd[i]==0)
{
r++;
q[r]=i;
}//如果点i的入度为0,那么就把r入队,队列右端点++
int cnt=n;
while (cnt!=0)//当所有点都输出了之后,循环结束
{
for (int j=1;j<=cd[q[l]];j++)
{
rd[p[q[l]][j]]--;
if (rd[p[q[l]][j]]==0)
{
r++;
q[r]=p[q[l]][j];
}
}
printf("%d ",q[l]);
cnt--;
l++;
}
return 0;
}
洛谷1983 车站分级的传送门 呼——这道题比较裸,可以练手用
好啦好啦写完啦【完结撒花✿✿ヽ(°▽°)ノ✿】
【蒟蒻wyf要把学过的所有算法都写成博客!】