首页 > 其他分享 >2022-11-25Acwing每日一题

2022-11-25Acwing每日一题

时间:2022-11-25 14:14:08浏览次数:77  
标签:11 输出 int 拓扑 入度 25Acwing 2022 序列 起点

本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

拓扑排序

给定一个 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

算法原理

先来介绍拓扑序列就是边的起点要在终点前输出。
image
如图,A到B这条边,A是起点,B是终点,所以先输出A在输出B,B到D这条边,B是起点,D是终点,所以先输出B在输出D,这时候我们发现C还没有输出,且A到C这条边,起点A已经被输出过,所以我们可以删掉已经被输出过的点,当然也要删掉这个点所能走的边,这样就只剩下C到D的边,C是起点,D是终点,先输出C后输出D,最后只剩下D这个点作为起点时才输出它。因此顺序为A->B->C->DA->C->B->D,你没看错,拓扑序列是可以存在多个的,但这道题只要求出其中一个即可,所以我们定义拓扑序列中先输出起点,在输出终点需要注意的是如果存在一个自环的话,那么一定不会存在拓扑排序
在上面分析的基础上,我们引入点的出度和入度。入度是指向该点的边的个数,出度是这个点指向其他结点的边的个数,这里用反证法进行证明:容易知道如果存在自环,即存在n点都至少具有一个出度和一个入度的话,那么从其中一个点沿着一条边走完所有的边就是n+1条边,由抽屉定理可知,其中一定会重复走过一个点,不仅表明了自环的存在,也说明这个终点在最开始被作为起点输出,这就不符合拓扑序列的定义,因此自环一定不存在拓扑序列,这个事实也告诉我们,拓扑序列先输出的一定是起点,也就是入度为0的点,并且拓扑序列的输出一定是n个而不是n+1个,然后从起点开始顺这它所连接的边不断往后走,输出一个点就删除上一个点到这个点的边,表示为让这个点的入度减一d[i]-1,也正由于队列只存储入度为0的点,并且只要使用过这个点作为起点遍历,那么这个点就再也不会入队,那么图中每个点一定只会在队列里出现过一次,我们可以额外用一个数组存储队列里存储过的点ans,并且其顺序就是拓扑序列的顺序。
以上是拓序列的主要思想,它能够用来判断图中是否存在自环。那么我们可以用什么数据结构实现呢,对于图的遍历我们可以使用BFS,对于这道题我们只需要入度,我们可以使用d[N]来表示第i个点的入度,具体的使用方法可以看代码注释哦。

代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

const int N = 100010;


queue<int> q;
int h[N],e[N],ne[N],idx;
int n,m;
int d[N];

void add(int a ,int b){
	e[idx] = b,ne[idx] = h[a],h[a]=idx++;
}


void bfs(){
	// 因为不是数组模拟队列,所以不会得到头指针和尾指针的移动状况,不能用这个来判断是否遍历了n个结点所以要用vector来存储结果
	vector<int> ans;	// 这就是存放答案的数组
	
	// 找入度为0的点并入队
	for(int i=1; i <= n; ++i){
		if(!d[i])	q.push(i);	// 将当前结点的编号放进去
	}

	// bfs进行拓扑序列
	while(!q.empty()){	// 判断队列不为空
		int t = q.front();
		q.pop();
		ans.push_back(t);
		for(int i=h[t] ; i != -1; i = ne[i]){
			int b = e[i];
			d[b]--;	// 先去边,在判断入度是否为0,因为到这个节点前就要把上一个上一个点到该点的一条边删掉,即入度-1
			if(!d[b]){	// 是起点就加进队列,这是因为后面删除边会使一些点变成起点
				q.push(b);
			}
		}
	}
	if(ans.size() == n)	// 如果不存在自环,n个点都可以作为起点
	{
		for(int i=0;i < ans.size() ; ++i){
			cout << ans[i] << ' ';
		}
		cout << endl;
	}
	else{	// 否则存在自环,不存在拓扑序列
		cout << "-1" << endl;
	}
}


int main(void){
	cin >> n >> m;
	memset(h,-1,sizeof h);
	for(int i=0; i < m ; ++i){
		int a,b;
		cin >> a >> b;
		add(a,b);	// a连接b
		d[b]++;	// b的入度+1
	}
	bfs();
	return 0;
}

标签:11,输出,int,拓扑,入度,25Acwing,2022,序列,起点
From: https://www.cnblogs.com/WangChe/p/16924924.html

相关文章

  • 日立750KC冰箱11月返场购真的值得入吗?
    隔离了34天,16天完全没下楼的博主,靠着这台日立冰箱里的食物储备硬是保证了自己的生活水平没有丝毫下降,而且胡吃海喝十几天之后,日立冰箱里仍然有多到不可思议的各种食材。......
  • 11.25.2
    #include<stdio.h>intmain(){ inta,n,i; unsignedlonglongsum=0; scanf("%d",&n); a=2; for(i=1;i<=n;i++) { sum+=a; a=a*10+a%10; } printf("%llu",sum)......
  • 英特尔® 酷睿™ i5-11300H 处理器
    https://www.intel.cn/content/www/cn/zh/products/sku/196656/intel-core-i511300h-processor-8m-cache-up-to-4-40-ghz-with-ipu/specifications.html......
  • C# 11 讲解二
    介绍接下来我将给大家重点介绍一下.Net6之后的一些新的变更,文章都是来自于外国大佬的文章,我这边进行一个翻译,并加上一些自己的理解和解释。源作者链接:https://blog.oky......
  • C# 11 讲解一
    介绍接下来我将给大家重点介绍一下.Net6之后的一些新的变更,文章都是来自于外国大佬的文章,我这边进行一个翻译,并加上一些自己的理解和解释。源作者链接:https://blog.oky......
  • HDC2022的无障碍参会体验,手语服务是如何做到的?
    华为开发者大会2022(HDC)上,HMSCore手语数字人以全新形象亮相,并在直播中完成了长达3个多小时的实时手语翻译,向线上线下超过一千万的观众提供了专业、实时、准确的手语翻译服......
  • 2022.11.24
    来了收拾了一阵QQ。鬼知道为什么收拾它。$$感觉今天出题出的很不地道,给我一种我能做出来的错觉,但是浪费了我的感情和时间。粘都懒得粘。$$我留下的奶茶大杯子被打......
  • [Typescript] 115. Hard - Drop String
    Dropthespecifiedcharsfromastring.Forexample:typeButterfly=DropString<'foobar!','fb'>//'ooar!' /*_____________YourCodeHere_____________......
  • 20221124
    为什么越来越有种感觉,最终肯定会分开.如果知道结局,还有必要继续下去吗?两个人都没问题。那问题出在哪呢?我太喜欢吃醋了,又是对谁都很活泼开朗的,如果一开始就不认识还好,......
  • 第11节-MySQL存储函数
    11.1、函数介绍1、函数是存储在服务器端的SQL语句的集合2、函数分为MySQL提供的内部函数和用户自定义医数两大类.MySQL提供了很丰富的内部函数·数学函数·字符串医......