C++拓扑排序

有向无环图

定义

没有环的有向图,也称为DAG

一个有向无环图

性质

性质一

从任意一点进行遍历,必不会陷入死循环

性质二

入度为0的点信息确定,新的入度为0的点信息也确定

拓扑序

写法

1.将入度为0的数入列;

2.将出队的节点的出边所指向的点的入度-1,如果该节点的入度也为0,那么将该节点入队;

3.当所有的点都出队,那么DAG的遍历结束

这样的遍历顺序被称为拓扑序

例题

题面

洛谷P1807icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1807

示例代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
const int M=3e6+5;
int n,m;
int h[N],v[M],w[M],nx[M],tot,du[N],dis[N];
void add(int x,int y,int z){
	tot++;
	v[tot]=y;//存出边指向的点 
	w[tot]=z;//存边的长度 
	nx[tot]=h[x];//指向下一组数据 
	h[x]=tot; 
}
long long read() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}//快读 
void topsort(){
	queue<int>q;//建立队列 
	for(int i=1;i<=n;i++){
		dis[i]=-2e9;//赋初值为-2e9 
		if(du[i]==0)	q.push(i);//如果入度为0那么入对
	}
	dis[1]=0;//1到1的距离设为0 
	while (!q.empty()){
		int x=q.front();//获得队列头 
		q.pop();//队列头出队 
		for(int i=h[x];i;i=nx[i]){
			du[v[i]]-=1;//出边所指向的点入度-1 
			if(dis[x]!=-2e9)	dis[v[i]]=max(dis[v[i]],dis[x]+w[i]);
			//若出队的点不为初始值,证明与1相通,那么更新该点到1的距离 
			if(du[v[i]]==0 )	q.push(v[i]);
			//如果入度为0则加入队列 
		}
	}
}
int main(){
	n=read();
	m=read();
	for(int i=1;i<=m;i++){
		int x,y,z;
		x=read();
		y=read();
		z=read();
		add(x,y,z);//邻接表存储 
		du[y]+=1;//出边所指向的点入读加1 
	}
	topsort();
    if(dis[n]<=-1e9){
		cout<<-1;
        return 0;
	}
	// 无法抵达则不会被更新
	cout<<dis[n];//输出1到n的距离 
	return 0;
}
​

拓扑排序

在DAG中,用拓扑序进行遍历,将结点的先后顺序输出就是拓扑排序;

 这张DAG有两种拓扑排序结果:
2->5->1->3->4

5->2->1->3->4

写法

我们只需要把出队后的点进行存储,再输出后就可以记录拓扑排序

例题

题面

输入

3 3
1 2
2 3
1 3

输出

1 2 3

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int h[N],v[N],nx[N],tot;
int dis[N],du[N];
priority_queue<int,vector<int>,greater<int> >q;
//优先队列(小根堆,也就是把队列内的数字从小往大排),因为是求最小字典序 
vector<int>ans;//动态数组存储拓扑排序 
void add(int x,int y){
	tot++;
	v[tot]=y;//存出边指向的点 
	nx[tot]=h[x];//指向下一组数据 
	h[x]=tot;
}
void bfs(){
	for(int i=1;i<=n;i++){
		dis[i]=-2e9;//赋初值为-2e9 
		if(du[i]==0)	q.push(i);//如果入度为0那么入对
	}
	while(!q.empty()){
		int x=q.top();//获得队列头
		q.pop();//队列头出队 
		ans.push_back(x); //将节点放入拓扑排序的内容中 
		for(int i=h[x];i;i=nx[i]){
			du[v[i]]--;//将出边指向的点的入度-1 
			if(du[v[i]]==0)	q.push(v[i]);//如果该点的入度为0则入队 
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		add(x,y);//邻接表存储 
		du[y]++;//出边指向的点入度加1 
	}
	bfs();
	if(ans.size()!=n){
		cout<<-1;
		return 0;
	}//如果拓扑排序访问过的数量小于或大于总点数,那么则没有拓扑排序 
	for(int i=0;i<ans.size();i++)	cout<<ans[i]<<' ';
	//输出最小字典序的拓扑排序 
	return 0;
}

 拓扑序解决层次问题

把节点分成若干层次,可以描述某一节点事件执行的优先级。

层次节点编号
11
22,3
34,5,6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值