给定一个n个点m条边的有向图,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-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
一道拓扑排序的模板题。拓扑排序的算法是,首先找到入度为0的点(所以需要一个数组to[]
维护每个结点的入度),将它放入队列,然后依次枚举它的所有出边t->j
,删掉它们之间的连接to[j]--
,如果此时to[j]==0
,那么将j
入队,持续这个过程直到队列为空。
代码:
#include <iostream> #include <queue> #include <memory.h> using namespace std; const int N = 1e5+10; int h[N], e[N], ne[N], idx; int to[N], ans[N]; int n, m; void add(int a, int b){ e[idx] = b; ne[idx] = h[a]; h[a] = idx++; //邻接表加入边的一般操作 } bool toposort(){ queue<int> q; int pp = 0; for(int i = 1; i <= n; ++i){ //首先找到入度为0的结点,将它加入队列和答案中 if(to[i] == 0){ q.push(i); ans[pp++] = i; break; } } while(!q.empty()){ int u = q.front(); q.pop(); //取出队首元素 for(int i = h[u]; i != -1; i = ne[i]){ //对每个u的出边 int v = e[i]; to[v]--; //删掉u->v的连接 if(to[v] == 0) { //如果v的入度为0了,那么加入队列和答案中 ans[pp++] = v; q.push(v); } } } return pp==n; } int main(){ scanf("%d%d", &n, &m); memset(h, -1, sizeof(h)); for(int i = 0; i < m; ++i){ int a, b; scanf("%d%d", &a, &b); add(a, b); //将a->b的边加入 to[b]++; //更新入度信息 } if(toposort()){ //如果有拓扑序,打印出结果 for(int i = 0; i < n; ++i) printf("%d ", ans[i]); }else printf("-1"); }