拓扑排序
简单介绍
我是在做有向图的题中,接触到拓扑排序的。拓扑排序,是对一个有向无环图进行排序,可以将所有顶点排列成一条线,(如果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;
}