拓扑排序

【定义】

对一个有向无环图Directed Acyclic Graph,简称DAG)G 进行拓扑排序,是将 G 中所有顶点排成一个线性序列,使得图中任意一对顶点 u 和 v,若边 (u,v)∈E(G),则 u 在线性序列中出现在 v 之前。通常,这样的线性序列称为满足拓扑次序Topological Order)的序列,简称拓扑序列

 

我们可以用一个比较形象的例子——选课,来理解这个东西。假设我非常想学习一门 A 课程,但是在修这个课程之前,我们必须要学习一些基础课程,比如 B,C,D 等等。那么这个制定选修课程顺序的过程,实际上就是一个拓扑排序的过程,每门课程相当于有向图中的一个顶点,而课程学习的先后关系就是连接顶点之间的有向边。

 

Kahn算法

Kahn算法的过程如下:

  1. 从图中选择一个入度为 0 的节点并输出
  2. 删除这个节点并且删除与这个节点相连的边
  3. 重复进行前两步,直到所有点都输出了,或者找不到入度为 0 的点了(有环)

我们用来维护一个点集,里面存储入度为 0 的节点

#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 200005
using namespace std;
int n,m,t;
stack<int>sta;
int v[M],next[M];
int du[N],first[N];
void add(int x,int y)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
}
int main()
{
	int x,y,i,num=0;
	scanf("%d%d",&n,&m);     //n是点数,m是边数 
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y); //从x到y的单向边 
		du[y]++;   //y的入度增加1 
		add(x,y);  //用前向星来存这张图 
	}
	for(i=1;i<=n;++i)
	  if(!du[i])
	    sta.push(i);  //将入度为0的点入栈 
	while(!sta.empty())
	{
		x=sta.top();    //取出栈顶元素 
		sta.pop();
		printf("%d ",x);     //输出栈顶元素 
		for(i=first[x];i;i=next[i])    //将所有与x相连的边删掉 
		{
			du[v[i]]--;      //v[i]的入度减1 
			if(!du[v[i]])
			  sta.push(v[i]);     //如果v[i]的入度为零,就将v[i]入栈 
		}
	}
	return 0;
}

 

【时间复杂度】

Kahn算法的时间复杂度为O(n+m),即点数+边数,嗯,非常优秀

 

【例题】

旅行计划

这道题其实也是一道DP题

用 f [ i ] 表示以 i 为终点最多能浏览的城市数量

由于只能往东走,考虑拓扑排序,删边的时候更新就可以了

#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 200005
using namespace std;
int n,m,t;
stack<int>sta;
int v[M],next[M];
int f[N],du[N],first[N];
void add(int x,int y)
{
        t++;
        next[t]=first[x];
        first[x]=t;
        v[t]=y;
}
int main()
{
        int x,y,i,num=0;
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;++i)
        {
                scanf("%d%d",&x,&y);
                du[y]++;
                add(x,y);
        }
        for(i=1;i<=n;++i)
            if(!du[i])
                f[i]=1,sta.push(i);
        while(!sta.empty())
        {
                x=sta.top();
                sta.pop();
                for(i=first[x];i;i=next[i])
                {
                        f[v[i]]=max(f[v[i]],f[x]+1);
                        du[v[i]]--;
                        if(!du[v[i]])
                            sta.push(v[i]);
                }
        }
        for(i=1;i<=n;++i)
            printf("%d\n",f[i]);
        return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值