欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

拓扑排序

程序员文章站 2023-12-23 16:53:10
...

一、什么是拓扑排序

在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。

有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。

例如,下面这个图:

 


拓扑排序

 

它是一个 DAG 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:

  1. 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
  2. 从图中删除该顶点和所有以它为起点的有向边。
  3. 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

 


拓扑排序

 

于是,得到拓扑排序后的结果是 { 1, 2, 4, 3, 5 }。

通常,一个有向无环图可以有一个或多个拓扑排序序列。

 

二、拓扑排序的应用

拓扑排序通常用来“排序”具有依赖关系的任务。

比如,如果用一个DAG图来表示一个工程,其中每个顶点表示工程中的一个任务,用有向边<A,B><A,B>表示在做任务 B 之前必须先完成任务 A。故在这个工程中,任意两个任务要么具有确定的先后关系,要么是没有关系,绝对不存在互相矛盾的关系(即环路)。

例题:

A - 确定比赛名次

 https://vjudge.net/contest/244053#problem/A

有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 4 3

代码:

#include <bits/stdc++.h>
const int maxn=1e3+10;
int p[maxn][maxn],vis[maxn];//p记录两点是否相连,vis记录点的入度
int n,m;
int flag[maxn][maxn];
void toposort()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(!vis[j])//如果入度为0(没有前驱点)
			{
				vis[j]--;//入度变成-1进行标记(入度为-1的点均为已经删除的点)
				printf("%d",j);//输出该节点
				if(i!=n)
					printf(" ");
				else
					printf("\n");
				for(int k=1;k<=n;k++)
				{
					if(p[j][k])
						vis[k]--;//将与j相连的节点都删去(删除与j有关的边)
				}
				break;
			}
		}
	}
}
int main(int argc, char const *argv[])
{
	int x,y;
	while(~scanf("%d%d",&n,&m))
	{
		memset(p,0,sizeof(p));
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&x,&y);
			//对数据进行处理,如果两点相连,p[x][y]=1,y的入度增加1
			if(!p[x][y])
			{
				p[x][y]=1;
				vis[y]++;
			}
		}
		toposort();
	}
	return 0;
}

 

上一篇:

下一篇: