拓扑排序


没有用的话qaq :
Ummmm…图论的大部分知识本来早就有学过,只是一直没有写成博文来梳理,但既然上了qbxt DP图论就写一篇来总结下,主要是来听DP的,但…由于太菜的原因,DP听得天花乱坠QWQ


有向无环图的拓扑排序是将DAG中的所有节点排序为线性序列,使得对于有向无环图的所有<s,e>∈E,都有s在线性序列中出现在u的前面。
有向图中若有环则一定不存在拓扑排序,若无则一定存在拓扑排序。


一,拓扑排序的实现
拓扑排序用栈和队列都可以实现,BFS和DFS也都可以实现。
首先对于拓扑排序的主要思想,其主要思想是对于一条有向边<s,e>,点s一定要在线性序列中出现在点e前面,所以我们在初始建图的时候统计一下每一个端点的入度,如果某个点的入度为0,那说明没有边指向它(约束它),它一定可以优先排到序列的开始的位置,将入读为零的点排入序列后,与这个点对这个点相邻的限制就会解除,因为这个点已经进入了序列,它在序列里的位置一定会比它指向的点靠前,所以我们就将所有邻点的入度-1,这样再次寻找入度为0的点循环往复,直到所有点都被拍完序。

算法流程:(这里仅描述bfs和队列实现的,其余原理均相同)
1,在建图的时候统计每个点的入度大小
2,初始寻找入度为零的点将其加入队列
3,只要队列不空,取队首元素加入序列,并扫描所有邻点,将其入度-1,若某个邻点入度变为0,将其加入队列
4,队列为空,排序结束

代码实现也很简单

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

struct node
{
	int ed,len,nxt;
};
node edge[2333];
int n,m,first[2333],ind[2333],cnt,a[2333],k;

inline void add_edge(int s,int e,int d)
{
	cnt++;
	ind[e]++;
	edeg[cnt].ed=e;
	edge[cnt].len=d;
	edge[cnt].nxt=first[s];
	first[s]=cnt;
	return;
}

inline void top()
{
	for(int i=1;i<=n;i++)
		if(ind[i]==0) q.push(i);
	while(!q.empty())
	{
		int p=q.front(); q.pop();
		a[++k]=p;
		for(int i=first[p];i;i=edge[i].nxt)
		{
			int e=edge[i].ed;
			ind[e]--;
			if(ind[e]==0) q.push(e);
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int s,e,d;
		scanf("%d%d%d",&s,&e,&d);
		add_edge(s,e,d);
	} 
	top();
	for(int i=1;i<=n;i++)
	    printf("%d ",a[i]);
	return 0;
}

二,拓扑排序相关ques
1,有n 项任务,有m 个限制,第i 个限制要求执行任务ui 之前必须要完成任务vi。请问是否存在合适的任务执行顺序,满足所
有的限制。如果有请输出一个

<法1>比较裸的加深拓扑排序印象的题目,其中限制对应DAG中的有向边,“第i 个限制要求执行任务ui 之前必须要完成任务vi”,就由ui向vi连一条边,首先这是一个有向图满足拓扑排序的性质1,其次题目中让我们求是否存在一个序列,前文说到过如果有向图有环就不存在拓扑排序,如果没有就存在,对于查找环我们可以遍历一遍整个图如果某一瞬间搜到了已经访问过的点,就证明有环,或者可以在拓扑排序的图中,如果有一瞬间还有点没有入队,但也找不到入度为0的点就有环证明不存在拓扑排序。对于这个ques为减少时间复杂度,建议用第二种方法。


2,有n 项任务,有m 个限制,限制有如下两种:
1, 执行u 任务之前必须要完成v 任务
2, 存在某一时刻,u 和v 任务都在执行
请问是否存在安排每个任务起始时间和结束时间的方案,满足所有的限制。

<法1>只要存在某一时刻,u和v任务都在进行即可,所以我们可以将一个任务拆成两个点,即第i个任务的开始时间点,第i个任务的结束时间点,起初要保证开始时间点要在结束时间点前,所以从开始时间点向结束时间点连一条边,再者满足条件,若要满足“ 执行u 任务之前必须要完成v 任务”,必须从u向v连一条边,“若要满足 存在某一时刻,u 和v 任务都在执行”,意为只要两个任务的时间有交集即可,为了保证两个事件的任务有交集就要保证事件v的开始要在事件u结束以前,然后事件u的开始在事件v的结束之前即可,这样两个时间段一定会有交集,所以就从u的开始向v的结束连一条有向边,再从v的开始向u的结束连一条有向边,即可,建完图后,像上道题一样,如果不存在环就有解,排一个线性序列即可,如果存在环,就无解。


3,对于带边权的有向无环图,求单源最短路。
因为是有向无环图嘛,直接dp在DAG上dp一下就好即dp[i]=min(dp[i的前驱]+边权),拓扑排序一下的话,如果出现在起点以前的点都是该点无法到达的点,所以最短路不存在喽就记为正无穷。


写在最后 Yousiki Orz Orz

原文地址:https://www.cnblogs.com/Hoyoak/p/11336507.html