拓扑排序(与动态规划)

求点赞~(录了个视频讲解在b站)

https://www.bilibili.com/video/BV1i44y137L2?spm_id_from=333.999.0.0icon-default.png?t=M3C8https://www.bilibili.com/video/BV1i44y137L2?spm_id_from=333.999.0.0

有向 无环 图(DAG):

-一张没有坏的有向图,写作DAG

-常见做动态规划

动态规划:

-将状态抽象成一个点,向更新的点连边

-满足两点:①无后效性②最优子结构

无后效性:若u走到v,那么u一定在v之前出现,或者说f[u]在f[v]之前就计算完成了。 比如说很简单的一个例子,我用dp[i]记录i之前有多少个正数,那么dp[i]是不关心dp[i+1]的,也就是说在计算dp[i+1]之前dp[i]就已经计算好了,就满足了无后效性。

所以为了dp中的无后效性,我们必须需要一种对图的顺序,就是拓扑序,排序的方法就是拓扑排序。注意,拓扑序不唯一

看个栗子:

那么对于这张图来说我的拓扑序就要求4在6前面,6在5前面,6在1前面,5在1前面,3在6前面,4在3前面,3在2前面,1在2前面,那么一个合法拓扑序就是 4-3-6-5-1-2

再举一个栗子:

 可能的合法拓扑序:ABCGDFE, AGBCDFE, etc.

这时我们发现:

 

 

 

 

 这些点入度为零的顺序就是一个拓扑序,也就有了拓扑排序算法。

算法复杂度:O(n+m)

算法步骤:

1. 找入度为零的点放入队列

2. 循环完成:

        ①取队首

        ②枚举连边

        ③删去连边(更新入度)

        ④新的入度为零的点放入队列

3. 出/入队顺序为拓扑序

如果有环:

 例如这图会在第一次删边后就找不到入度为零的节点,那么出队的节点数量就不到n个

所以——拓扑序可以用来判别图中是否有环

上代码:

//in数组记录入度,g_e为链式前向星记录边,g_q为队列,r为记录拓扑序的数组
void topo_sort()
{
	for(int i=1;i<=n;++i)
	{
		if(in[i]==0)
		{
			g_q.push(i);
		}
	}
	while(!g_q.empty())
	{
		int tmp=g_q.front();
		g_q.pop();
		r[++tot]=tmp;
		for(int i=head[tmp];i;i=g_e[i].nxt)
		{
			int v=g_e[i].v;
			in[v]--;
			if(in[v]==0)
			{
				g_q.push(v);
			}
		}
	}
}

用一道例题:

说明

有向无环图上有n个点,m条边。求这张图字典序最小的拓扑排序的结果。字典序最小指希望排好序的结果中,比较靠前的数字尽可能小。

输入格式

第一行是用空格隔开的两个整数n和m,表示n个点和m条边。

接下来是m行,每行用空格隔开的两个数u和v,表示有一条从u到v的边。

1≤n,m≤100 000

注意:图上可能有重边

输出格式

输出一行,拓扑排序的结果,数字之间用空格隔开

样例

输入数据 1

5 3
1 2
2 4
4 3

输出数据 1

1 2 4 3 5

 这道题让字典序最小,那么解决方案就是使用priority_queue小根堆,每次出队选择编号最小的就可以解决了

上代码:

#include<bits/stdc++.h>
#define MAXN 200010

using namespace std;

int n,m,u,v,cnt=0,in[MAXN],r[MAXN],head[MAXN],tot=0;
priority_queue<int, vector<int>, greater<int> > g_q; 

struct tEdge{
	int u,v,nxt; 
}g_e[MAXN];

void addedge(int u,int v)
{
	g_e[++cnt].u=u,g_e[cnt].v=v;
	g_e[cnt].nxt=head[u],head[u]=cnt;
}

void topo_sort()
{
	for(int i=1;i<=n;++i)
	{
		if(in[i]==0)
		{
			g_q.push(i);
		}
	}
	while(!g_q.empty())
	{
		int tmp=g_q.top();
		g_q.pop();
		r[++tot]=tmp;
		for(int i=head[tmp];i;i=g_e[i].nxt)
		{
			int v=g_e[i].v;
			in[v]--;
			if(in[v]==0)
			{
				g_q.push(v);
			}
		}
	}
	for(int i=1;i<=tot;++i)
	{
		printf("%d ",r[i]);
	}
}

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d %d",&u,&v);
		addedge(u,v),addedge(v,u);
		in[v]++;
	}
	topo_sort();
	return 0;
}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值