HDU - 1285 (拓扑排序)
有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
- 拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。这是百度百科上的解释,我理解的就是在某些情况下只有前面的事做完了才能做后面的事 然后让你找一个完整的步骤。 - 解题步骤:
a.先把图用邻接表的形式存下来
b.然后找入度为0 的点入队,小细节的地方就是这个题要用优先队列,因为答案可能有很多种,题目要求小的数在前面,所以每次取的队首元素都是队列里面最小的。
c.取出队首这个点后,把这个点连接的点的入度都减一,每次判断一下入度为0吗,为0就入队。用一个ans数记录一下答案(这个ans数组还能判断有没有正确答案,最后如果数组里的元素少于n说明无解)
d.一直到队列为空为止,输出ans数组。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
struct node{
int id;
int d;
int next;
}side[maxn];///记录一条边 id-next权值为d
int head[maxn];
int cnt=0;
void init(int n)
{
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int x,int y)
{
side[cnt].id=y;
side[cnt].d=0;
side[cnt].next=head[x];///头部插入
head[x]=cnt++;
}
int in[maxn];///用来记录入度
int ans[maxn];///用来记录答案 防止没有正确排序的情况,但是这个题保证一定有 所以可以不用这个
int main()
{
int n,m;
while(cin>>n>>m)
{
init(n);
memset(in,0,sizeof(in));
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);//连接x和y构成一条边 因为没有权值 权值可以随意赋值
in[y]++;//y的入度+1;
}
priority_queue <int, vector<int>,greater<int> > qq;////建一个优先队列
for(int i=1;i<=n;i++)
{
if(in[i]==0)///把起初入度为0的点入队
qq.push(i);
}
int k=0;
while(qq.size())
{
int x0=qq.top();///入度为0的最小的点
ans[k++]=x0;///记录下答案
qq.pop();///记录完了就吧这个出队
for(int i=head[x0];i!=-1;i=side[i].next)
{
int y0=side[i].id;
in[y0]--;
if(in[y0]<=0)
qq.push(y0);
}
}
for(int i=0;i<k;i++)
{
printf(i == k-1 ?"%d\n":"%d ",ans[i]);
}
}
return 0;
}