拓扑排序(介绍+例题 可达性统计)

拓扑排序

简单介绍

我是在做有向图的题中,接触到拓扑排序的。拓扑排序,是对一个有向无环图进行排序,可以将所有顶点排列成一条线,(如果a指向b,则a排在b的前面),****需要注意,这个排序只针对无环图。

有向无环图

请添加图片描述
通过拓扑排序得到符合条件的情况一般不止一种,例如这个排序后:
1 4 2 3 5 6
1 2 3 4 5 6
1 2 4 3 5 6
2 3 1 4 5 6
2 1 3 4 5 6
2 3 1 4 5 6
通常会根据字典序输出。

邻接表加边

如果为有向图的题目,我们需要将题目中给出的边加在图中,感觉与链表添加元素类似

基本框架

void add(int u,int v)
{
	e[idx]=v;//数组e存边的终点
	ne[idx]=head[u];//将头节点连接到当前位置
	head[u]=idx++;//将头节点与终点相连,并且将当前位置后移,为下次添加边提供便利
}

拓扑排序基本思想

1.将入度为0的入队列
2.如果队列不为空,就依次循环以下步骤:
3.每次取出队首元素,找与它相连的元素
4.将该元素的入度-1
5.如果此时度为0,则入队列

基本框架

void topsort()
{
	queue<int>q;//创建队列每次存储入度为0的节点
	for(int i=1;i<=n;i++)
	{
		if(!d[i])
		{
			q.push(i); //入度为0的入队列
		}
	}
	int k=0;
	while(q.size())
	{
		int t=q.front();
		q.pop();//每次取队首,并且将其弹出队列
		seq[k++]=t;
		for(int i=head[t];~i;i=ne[i])//依次遍历与当前点连接的点
		{
			int j=e[i];
			if(--d[j]==0)
			q.push(j);//入度为零入队
		}
	}
}

例题 可达性统计

原题链接

题意描述:

给出N个点,M条边的有向无环图,输出从每个点出发能够到达的点的的数量

解题思路

1.将每条边连接起来,即加边
2.拓扑排序,使得每条的起点都在终点之前
3.在计算有多少个点时,由于数据范围也比较大,我们可以用bitset来统计
(有关bitset可以参考 点这

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define N 30010
int n,m;
int head[N],e[N],ne[N],idx;
int d[N],seq[N];
bitset<N>b[N];
void add(int u,int v)
{
	e[idx]=v;//数组e存边的终点
	ne[idx]=head[u];//将头节点连接到当前位置
	head[u]=idx++;//将头节点与终点相连,并且将当前位置后移,为下次添加边提供便利
}
void topsort()
{
	queue<int>q;//创建队列每次存储入度为0的节点
	for(int i=1;i<=n;i++)
	{
		if(!d[i])
		{
			q.push(i); //入度为0的入队列
		}
	}
	int k=0;
	while(q.size())
	{
		int t=q.front();
		q.pop();//每次取队首,并且将其弹出队列
		seq[k++]=t;
		for(int i=head[t];~i;i=ne[i])//依次遍历与当前点连接的点
		{
			int j=e[i];
			if(--d[j]==0)
			q.push(j);//入度为零入队
		}
	}
}
void solve()
{
	cin>>n>>m;
	memset(head, -1, sizeof(head));
	for(int i=0;i<m;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v);
		d[v]++;
	}
	topsort();
	for(int i=n-1;i>=0;i--)
	{
		int j=seq[i];
		b[j][j]=1;
		for(int k=head[j];~k;k=ne[k])
		{
			b[j]|=b[e[k]];//通过按位或可以让出现的结果的位置由0->1,最终统计1的个数即可
		 } 
	}
	for(int i=1;i<=n;i++)
	{
		cout<<b[i].count()<<"\n";
	 } 
}
signed main()
{
	IOS
	int t=1;
	while(t--)
	solve();
	return 0;
}
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值