拓扑排序
拓扑排序要解决的问题是给一个图的所有节点排序。在一个有向无环图DAG中,我们将图中的顶点以线性的方式进行排序,对于有向边(u,v),确保u在v的前面,认为v依赖于u; 如果u到v有路径,u可达v,称v间接依赖于u
拓扑排序可以用于解决先导课,途经车站等可以转化为有向图的题目。如果有向图中出现了环,就没有办法进行拓扑排序了
Kahn算法
- 对于一个图,首先将所有入度为0的点组成一个集合S(S通常使用队列表示)
- 每次从S中取出一个点 u 放入 L ,遍历以 u 为出发点的边 (u,v)并进行删除,如果在删除了该边后,顶点 v 的入度变为 0 ,就将顶点 v 放入集合S中。
- 不断重复上述过程知道集合为空
- 如果最后集合为空的时候图中还存在边,则图一定存在环路,它无法进行拓扑排序;如果没有边,输出L,L中的顺序就是拓扑排序的结果
比如对于下面这个图:
第一步就是寻找入度为0的点将他放到队列中,将2,8 放入;
然后取出2 开始寻找2能到达的点,先删除边(2,0),在删除了这条边后0的入度变为0,所以将0加入队列;
继续删除边(2,3),删除后3的入度变为0,将3加入队列;
然后再从队列中取出8开始重复过程…例题:名次排序
一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。现在我们知道每场比赛的结果,请你编程序确定字典序最小的名次序列
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫
给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名
sample input:
4 3
1 2
2 3
4 3
sample output:
1 2 4 3
思路:
- 因为给出了所有的胜负关系,所以可以构建一张有向无环图
- t1 胜 t2 等价于图中存在一条 (t1,t2) 边
- 因为要求最终输出字典序最小的拓扑排序,所以使用优先级队列
- 拓扑排序的思路就是Kahn算法的思路
- 刚开始答案一直不对是因为在初始化入度的时候从0开始了,但实际上这道题并不存在0号顶点,就会导致输出答案的错误
#include<iostream>
#include<cstdio>
#include<string.h>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
const int TOP=5e3+2;
int N,M;
struct edge
{
int to,next;
}e[TOP];
int head[TOP],in_deg[TOP],tot;
priority_queue<int,vector<int>,greater<int> >q;
vector<int> ans;
void add(int t1,int t2)
{
e[tot].to=t2;
e[tot].next=head[t1];
head[t1]=tot;
in_deg[t2]++;
tot++;
}
void toposort()
{
for(int i=1;i<=N;i++)//这里有个坑orz
{
if(in_deg[i]==0)
q.push(i);
}
bool visit[TOP];
memset(visit,true,sizeof(visit));
while(!q.empty())
{
int u=q.top();
q.pop();
ans.push_back(u);
for(int i=head[u];i!=-1;i=e[i].next)
{
int temp=e[i].to;
if(visit[i])
{
in_deg[temp]--;
if(in_deg[temp]==0)
q.push(temp);
visit[i]=false;
}
}
}
cout<<ans[0];
for(int i=1;i<N;i++)
cout<<" "<<ans[i];
cout<<endl;
}
int main()
{
while(scanf("%d%d",&N,&M)!=EOF)
{
tot=0;
memset(head,-1,sizeof(head));
memset(in_deg,0,sizeof(in_deg));
ans.clear();
while(M--)
{
int t1,t2;
scanf("%d%d",&t1,&t2);
add(t1,t2);
}
toposort();
}
return 0;
}