首页 > 其他分享 >拓扑排序学习笔记

拓扑排序学习笔记

时间:2023-08-25 11:14:13浏览次数:48  
标签:int 拓扑 入度 笔记 queueq 100001 fat bool 排序

思想

拓扑,一看就是从图的开始开始开拓,并按被开拓到的顺序排序

拓扑排序的思想如下:

将入度为 \(0\) 的点删除,并记录它被删除的顺序,直到没有点则结束程序

图解

image
如本图的拓扑序就为 1 2 3 5 4

1.发现1入度为0

删除1,2/3的入度减\(1\)
image

2.发现2入度为0

删除2,5的入度减\(1\)
image

3.发现3入度为0

删除3,5的入度减\(1\)
image

4.发现5入度为0

删除5,4的入度减\(1\)
image

5.发现4入度为0

删除4,发现没有点了,结束程序

全程

image

代码

代码也十分简单(甚至比思维还简单):

#include<bits/stdc++.h>
using namespace std;
bool b[100001];
int fat[100001];
vector<int>v[100001];
int main(){
	int n,m=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		v[x].push_back(y);
		fat[y]++;//记录入度
	}
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(fat[i]==0){//将入度为0的点入队
			q.push(i);
			b[i]=1;
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<v[u].size();i++){
			int nn=v[u][i];
			fat[nn]--;//入度减少
			if(fat[nn]==0){
				q.push(nn);//将入度为0的点入队
				b[nn]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(!b[i])cout<<i<<" ";	//也可以判断环上的点
	}
	return 0;
}

例题

P8655 [蓝桥杯 2017 国 B] 发现环

点击查看代码
#include<bits/stdc++.h>
using namespace std;
bool b[100001];
int fat[100001];
vector<int>v[100001];
int main(){
	int n,m=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x);
		fat[x]++;
		fat[y]++;
	}
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(fat[i]==1){
			q.push(i);
			b[i]=1;
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<v[u].size();i++){
			int nn=v[u][i];
			fat[nn]--;
			if(fat[nn]==1){
				q.push(nn);
				b[nn]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(!b[i])cout<<i<<" ";	
	}
	return 0;
}

B3644 【模板】拓扑排序 / 家谱树

字典序最小

有时会有要字典序最小的情况,这是开个大根堆即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
bool b[100001];
int fat[100001],ans[100001];
vector<int>v[100001];
int main(){
	int T=0;int n,m=0;
	cin>>T;
	while(T--){
		for(int i=1;i<=n;i++){
			fat[i]=0;
			v[i].clear();
			b[i]=0;
		}
		cin>>n>>m;
		for(int i=1;i<=m;i++){
			int x,y;
			cin>>x>>y;
			v[y].push_back(x);
			fat[x]++;
		}
		priority_queue<int>q;
		for(int i=1;i<=n;i++){
			if(fat[i]==0){
				q.push(i);
				b[i]=1;
			}
		}
		int tp=0;
		while(!q.empty()){
			int u=q.top();
			tp++;
			ans[tp]=u;
			q.pop();
			for(int i=0;i<v[u].size();i++){
				int nn=v[u][i];
				fat[nn]--;
				if(fat[nn]==0){
					q.push(nn);
					b[nn]=1;
				}
			}
		}
		if(tp<n){
			cout<<"Impossible!"<<endl;
			continue;
		}
		for(int i=n;i>=1;i--){
			cout<<ans[i]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

唯一解问题

思路一:开三个不同记录方式的堆

bool check(int kk){
	queue<int>q;
	priority_queue<int>q1;
	priority_queue<int, vector<int>, greater<int> >q2;
	int op=0;
	for(int i=1;i<=n;i++){
		ru[i]=0;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<v[i].size();j++){
			ru[v[i][j].to]++;
		}
	}
	for(int i=1;i<=n;i++){
		if(!ru[i]){
			q.push(i);
			q1.push(i);
			q2.push(i);
		}	
	}
	while(!q.empty()){
		int u=q.front(),u1=q1.top(),u2=q2.top();
		q.pop();q1.pop();q2.pop();
		if(u!=u1||u!=u2||u1!=u2)return 0;
		op++;
		for(int i=0;i<v[u].size();i++){
			int nn=v[u][i].to;
			ru[nn]--;
			if(!ru[nn]){
				q.push(nn);
				q1.push(nn);
				q2.push(nn);
			}
		}
	}
	//cout<<kk<<"/"<<op<<endl;
	if(op==n)return 1;
	else return 0;
}

思路二:队内判断

很容易证明只有队内只有一个数时才有唯一解,代码如下:

bool check(int kk){
	queue<int>q;
	int op=0;
	for(int i=1;i<=n;i++){
		ru[i]=0;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<v[i].size();j++){
			ru[v[i][j].to]++;
		}
	}
	for(int i=1;i<=n;i++){
		if(!ru[i]){
			q.push(i);
		}	
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		if(!q.empty())return 0;
		op++;
		for(int i=0;i<v[u].size();i++){
			int nn=v[u][i].to;
			ru[nn]--;
			if(!ru[nn]){
				q.push(nn);
			}
		}
	}
	if(op==n)return 1;
	else return 0;
}

标签:int,拓扑,入度,笔记,queueq,100001,fat,bool,排序
From: https://www.cnblogs.com/ccr-note/p/topu.html

相关文章

  • 最小生成树学习笔记
    Prim算法prim算法基本思想:基于点的解决方式先随便选择一个点s作为起点,把其他所有点设为未添加节点,再设一dis数组,代表每个节点到最小生成树最近点的距离,易得一开始只有dis[s]=0,其他均为∞。每轮找到dis值最小且未添加过的节点加入生成树中,且使用这个节点的邻边去更新它的邻点......
  • 并查集学习笔记
    并查集的定义并查集是一种树型的数据结构,用于处理一些不相交集合(disjointsets)的合并及查询问题。常常在使用中以森林来表示。——百度百科并查集,顾名思义,支持以下两种操作操作:并(Union):把两个不相交的集合合并为一个集合。查(Find):查询两个元素是否在同一个集合中。并......
  • 字典树学习笔记
    字典树字典树(Trie)简介又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比......
  • 执行排序
    排序方式方法排序类排序Suite方法排序的类型类型说明OrderAnnotation(重点)@Order 注解指定排序DisplayName根据显示名称排序Random随机排序MethodName根据方法名称排序importorg.junit.jupiter.api.MethodOrderer.OrderAnnotation;importorg.ju......
  • 《深入理解Java虚拟机》读书笔记:方法调用
      方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。在程序运行时,进行方法调用是最普遍、最频繁的操作,但前面已经讲过,Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件......
  • 赵老师 计数原理 课程笔记
    计数原理分类加法计数原理与分步乘法计数原理分类加法计数原理引例题干用一个大写的英文字母或一个阿拉伯数字给教室里的一个座位编号,总共能编出多少种不同的号码?解决因为英文字母共有\(26\)个,阿拉伯数字共有\(10\)个,所以总共可以编出\(26+10=36\)种不同的号......
  • Programming abstractions in C阅读笔记:p127-p129
    《ProgrammingAbstractionsInC》学习第51天,p127-p129,总结如下:一、技术总结1.stringlibrary掌握常用函数如strlen,strcpy用法。2.bufferoverflow(缓冲区溢出)(1)什么是buffer?p129,Arraysthatarepreallocatedandlateruseasarepositoryfordatacalledbuffers......
  • 莫队学习笔记
    学习莫队是非常有必要的众所周知,莫队是一种优越的暴力算法,当我们在\(NOIP\)等考试中数据结构不会打且问题是离线时,我们就可以:莫队,启动!好,切入正题,我们现在来看看莫队是什么:例题传送门简要题意:给定一个长度为\(n\)的序列\(a\),然后再给一个数字\(k\),再给出\(m\)组询问,每......
  • 「学习笔记」浅入模拟退火
    什么是退火?来自百度百科退火是一种金属热处理工艺,指的是将金属缓慢加热到一定温度,保持足够时间,然后以适宜速度冷却。目的是降低硬度,改善切削加工性;降低残余应力,稳定尺寸,减少变形与裂纹倾向;细化晶粒,调整组织,消除组织缺陷。准确的说,退火是一种对材料的热处理工艺,包括金属材料、非......
  • YTEZ校内数学集训笔记
    计数原理例题1:用一个大写的英文字母或一个阿拉伯数字给教室里的一个座位编号,总共能编出多少种不同的号码?或:\(a\wedgeb\)有\(a\)无\(b\)有\(b\)无\(a\)有\(a\)有\(b\)且:\(a\veeb\)有\(a\)有\(b\)非:\(┐a\)无\(a\)答案:英文字母共有26个,阿拉伯数......