拓扑排序
一、什么是拓扑排序
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
- 每个顶点出现且只出现一次。
- 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。
例如,下面这个图:
它是一个 DAG 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:
- 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
- 从图中删除该顶点和所有以它为起点的有向边。
- 重复 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;
}