acwing 848. 有向图的拓扑序列

题目描述

给定一个 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≤10^5

输入样例:

3 3
1 2
2 3
1 3

输出样例:

1 2 3

拓扑排序算法求解

分析

d[u]记录每个节点u的入度

维护一个队列q,把所有入度为0的点放入队列中

每次从队列中取一个点,遍历该点所有的邻接点,并将每个邻接点的入度-1,如果-1后该邻接点的入度也变成了0,那么将其加入队列

同时使用res[N]记录每个进入过队列中的点

如果队列为空时,res的大小为n说明存在拓扑排序,输出即可;否则说明存在环

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int N = 100010, M = 2*N;

int n, m; 
int d[N];// 存每个节点的入度 
queue<int> q; //存所有入度为0的点 
int res[N], cnt = 0;
//存图
int h[N], e[M], ne[M], idx = 0;
void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

void tor()
{
	// 先把入度为0的点加入队中 
	for(int i = 1; i <= n; i++)
		if(!d[i])
		{
			q.push(i);
			res[cnt++] = i;		
		}
	
	while(q.size())
	{
		int t = q.front();
		q.pop();
		
		// t所有指向的点 
		for(int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			
			d[j]--; // 该点入度-1 
			if(!d[j]) 
			{
				q.push(j); // 若度为0,加入队列 
				res[cnt++] = j;	
			}
		}
	}
}

int main()
{
	memset(h, -1, sizeof h);
	scanf("%d%d", &n, &m);
	for(int i = 0; i < m; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b);
		d[b] ++;
	}
	
	tor();
	
	if(cnt < n) cout << -1 << endl;
	else {
		for(int i = 0; i < n; i++)
		cout << res[i] << " ";
	}
	puts("");
	
	return 0;
}

时间复杂度

参考文章

posted @ 2022-02-27 10:33  VanHope  阅读(39)  评论(0编辑  收藏  举报