拓扑排序入门

                                               拓扑排序入门

下面先给一道题,然后先思考一下,下面再开始介绍拓扑排序,

有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。 

Input

输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。 

Output

给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。 

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。 

Sample Input

4 3
1 2
2 3
4 3

Sample Output

1 2 3 4

 乍一看是不是没有思路,不知道从何下手,那是因为你还没有学习拓扑排序,那什么是拓扑排序呢?对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任
意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。


无向图和有环的有向图没有拓扑排序。

简单的说吧,就这个题而言,就是先把这一道题转化成一个有向图的形式,如果是 1赢了2,那么这个条件转化成一个图的边,另

1——>2,然后再把2这个节点的入度加1,然后下面依次进行,那么想一想,是不是输的场次越多,那么这个节点的入度就会越多,所以说,就当前图的状态而言,那个入度为零的点一定是最厉害的,为什么会这么说呢?因为如果这个节点的入度不为零,那么证明有人赢了他,那么此时他就不是最厉害的了,所以说此时图中入度为零的节点就是最厉害的那个。

好了,现在我们已经找到最厉害的了,下面我们就要开始找第二厉害的了,这个怎么找的,首先我们已经找到老大了,那么剩下的就不关他的事了,所以他打败的那些人的线是不是都可以去掉了呢?当然可以,因为老大已经是老大了,他没有必要再欺负小弟了是吧?然后依次往下推,直到给所有的人都排上序,这就是拓扑排序的大致思路吧。

下面来模拟一下大致过程:

首先下面是一个有向无环图:

然后我们先来分析一下这个图,首先很容易找到老大,就是入度为零的点,就是V1和V6找到老大后,接下来我们就要把这个老大给去掉,就随机去掉一个V6吧,然后图就变成了:

此时入度为零的点就是V1,然后把V1去掉,依次类推:

然后去掉V4:

然后去掉V3:

然后就剩下了两个,这样排序就排好了,v6 v1 v4 v3 v2 v5;

大致思路大概我们都已经懂了,然后我们就要开始拓扑排序代码的实现了;

首先我们先要把图表示出来,首先我们要用一个二维数组表示两个点之间是否有边,如果x和y之间有边那么a[x[y]=1,否则就等于0;边是表示好了,还有一个问题啊,就是没个点的入度啊,我们再定义一个一维数组A[]来表示每个点的入度,如果有边只向x那么就让A[x]++;          下面就是AC代码:


#include<stdio.h>
#include<string.h>
int a[505][505];
int A[505];
int n,m;
void toposort()
{
	int cnt=0;
	for(int i=1;i<=n;i++)/*要遍历n次*/
	{
		for(int j=1;j<=n;j++)
		{
			if(A[j]==0) /*找入度为0的点*/
			{
				if(cnt==0)
				{
					printf("%d",j);
					cnt=1;
				}
				else printf(" %d",j);
				A[j]=-1;
				for(int k=1;k<=n;k++)
				{
					if(a[j][k]!=0)/*找和此时的老大有边的点,如果和此时的老大相连,那么就把这个点的入度减1*/
					{
						A[k]--;
					}
				}
				break;
			}
			
		}
	}
}
int main()
{
	int x,y;
	while(~scanf("%d %d",&n,&m))
	{
		memset(a,0,sizeof(a));
		memset(A,0,sizeof(A));
		for(int i=1;i<=m;i++)
		{
			scanf("%d %d",&x,&y);
			if(a[x][y]==0)
			{
				a[x][y]=1;/*如果此时没有这两个点之间没有这个边,那么就副职为1*/
				A[y]++;//把这个点的入度加1;
			}
		}
		toposort();
		printf("\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42757965/article/details/82014363