Acwing 848. 有向图的拓扑序列
1、拓扑定义: 若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。
通俗来说:当这个图的所有点排成一个序列时, 所有边的箭头都是往后指的。
所以,拓扑序不是唯一的。
2、有向无环图一定有拓扑序列。所以 有向无环图 又称 拓扑图。
有向无环图一定至少存在一个 入度 为0 的点;
(反证:假如所有的入度都不是0,那么就可以无穷尽地找下去, 会找到n + 1 个点, 但是只有n 个点, 所以有两个点是一样的。)
3、拓扑思路:
宽搜;{
*1定义队列q;
*2while q 不空{
*3 取队头 t;
*4 枚举t所有的出边t -> j;
*5 取得这条边的终点 j;
*6 删掉t -> j这条边,也就是取消 j 的一个入度,即 d[j] - - ;
*7 if( d[j] == 0)说明 没有点可以到 j 了;
*8 j -> q;j 入队。
}
*9return ~;按要求返回;
}
例题
给定一个n个点m条边的有向图,点的编号是1到n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-1。
若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。
输入格式
第一行包含两个整数n和m
接下来m行,每行包含两个整数x和y,表示存在一条从点x到点y的有向边(x, y)。
输出格式
共一行,如果存在拓扑序列,则输出拓扑序列。
否则输出-1。
数据范围
1≤n,m≤105
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
对应模板
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];
void add( int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool topsort(){
int hh = 0, tt = -1;
for(int i = 1; i <= n; i ++) /* *1 */
if(!d[i])
q[++ tt] = i;
while(hh <= tt){ /* *2 */
int t = q[hh ++]; /* *3 */
for(int i = h[t]; i != -1; i = ne[i]){ /* *4 */
int j = e[i]; /* *5 */
d[j] --; /* *6 */
if(d[j] == 0) /* *7 */
q[++tt] = j; /* *8 */
}
}
return tt == n - 1; /* *9 */
}
int main(){
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i =0; i < m; i ++){
int a, b;
cin >> a >> b;
add(a, b);
d[b] ++;
}
if(topsort()){
for(int i = 0; i <n; i ++)
cout << q[i] << " ";
}
else cout << "-1" <<endl;
return 0;
}
————————————————————————————————————