拓扑排序(Topological sorting)

拓扑排序指的是有向无环图(DAG);

学过计算机网络的知道计算机网络中有一个拓扑结构;

下面就是一个拓扑结构;

那拓扑序就是,图中任意一对顶点u和v,若边∈E(G),则u在线性序列中出现在v之前

我们可以发现拓扑序不是唯一的;

接下来,我们需要知道一个概念——

对于有向图的某个结点来说,我们把指向它的边的数量叫做入度

把从它发出的边的数量称为出度,这个都很好理解吧;

拓扑排序(Topological sorting)_第1张图片

如何获得一个拓扑序: 

我们先来找一个最容易理解的例子,那就是食物链,对一个自然界的食物链来说,一定存在拓扑序;

第一:不存在环;

第二:有向;

接下来我们来看看如何获得一个拓扑序,我们观察最左面的结点,一定是没有指向它的边,也就是入度为零,最右边的结点呢,是一定不存在它指向别人的边的,如果有,那么它就不是最右边的点也就是出度为零;

那就是说,所有入度为零的点都可以作为起点,我们开一个数组记录入度的值;

至于如何使用邻接表存边,这就不展开解释了,可以参考这个:传送门

其实,拓扑排序也是bfs的一个简单应用,我们需要借助队列来实现;

首先,我们遍历存储入度的数组,获得可以作为起点的结点,将其加入队列;

接下来就可以愉快的遍历了,没当我们遍历到一个点的时候,我们让它的入度--;

这样做的意义就是,判断指向这个点的边是不是都遍历过了,因为我们要保证拓扑序最重要的一个特点:的边中,u一定在v的前面出现;

如果这个点所有的边都遍历过的话,是不是也就是说这个点已经没有指向它的边了,也就是说这个点可以作为一个起点了,那我们将它加入队列;循环这个操作,知道队列为空;

最大食物链计数 - 洛谷

有了上面的介绍,我们直接来看这个题;我们要求最大食物链数量;

这个最大,我们不要理解错了,这个指的就是,我们需要从一个入度为零的点开始到一个出度为零的点;计算给出的食物网中,食物链的数量;

这里题,我们需要不仅需要记录一下入度还要记录一下出度,因为我们要计算食物链的数量,食物链的最后一个结点,是不是就是出度为零的点;

然后食物链的数量的话,是不是就是一个简答的dp啊;然后我们这个题就做完了;

#include
#include
#include
#include
#include
#define x first
#define y second 
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);

using namespace std;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair PII;

const int N = 5010,M = 500010,INF = 0x3f3f3f3f,mod = 80112002;

int h[N],e[M],ne[M],idx;
int d[N],c[N],f[N],q[N];
int n,m;

void add(int a,int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

void topsort()
{
	int hh = 0,tt = -1;
	
	for(int i =1 ;i<= n;i++)
	{
		if(!d[i])
		{
			q[++tt] = i;
			f[i] = 1; 
		}
	}
	
	while(hh <= tt)
	{
		int t = q[hh++];
		
		for(int i = h[t]; i!= -1 ; i = ne[i])
		{
			int j = e[i];
			d[j]--;
			f[j] = (f[j] + f[t]) % mod;
			
			if(d[j] == 0)q[++tt] = j; 		
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	
	memset(h,-1,sizeof h);
		
	for(int i = 1 ;i<= m ;i ++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);
		d[b]++,c[a]++;
	}
	
	topsort();
	
	int res = 0;
	
	for(int i = 1; i<= n ;i++)
	{
		if(!c[i])
		{
			res = (res + f[i]) % mod;
		}
	}
	printf("%d",res);
	
	return 0;
} 

旅行计划 - 洛谷

这个题,我们根据题意是不是知道这个是一个DAG,我们需要计算的是以城市 i 为终点最多能够游览多少个城市;这个是不是也是在一个拓扑序上做一个简单的dp就行了,我们记录一下最大值即可;

#include
#include
#include
#include
#include
#define x first
#define y second 
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);

using namespace std;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair PII;

const int N = 100010,M = 2 * N ,INF = 0x3f3f3f3f;

int n,m;

int h[N],e[M],ne[M],idx;

int d[N],q[N],f[N];

int hh = 0, tt = -1;

void add(int a,int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx ++;
} 

void topsort()
{
	for(int i =1 ;i<= n ;i ++)
	{
		if(!d[i])
		{
			q[++tt] = i;
			f[i] = 1;
		}
	}
	
	while(hh <= tt)
	{
		int t = q[hh++];
		
		for(int i =h[t]; i != -1 ;i = ne[i])
		{
			int j = e[i];
			f[j] = max(f[j] , f[t] + 1);
			d[j]--;
			if(d[j] == 0)q[++tt] = j;
		}
	}
	
}

int main()
{
	scanf("%d%d",&n,&m);
	
	memset(h,-1,sizeof h);
	 
	for(int i = 1;i <= m ;i ++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);
		d[b] ++;
	}
	
	topsort();
	
	
	for(int i =1; i<= n; i++)
	{
		printf("%d\n",f[i]);
	}
	
	return 0;
} 

你可能感兴趣的:(图论,算法,c++,图论,数据结构)