2019/4/22 拓扑排序的高效写法. 模板题HDU1285:确定比赛名次

传送门

Problem Description

有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

 

这里默认为大家都已经了解了拓扑排序的思想和基本算法, 矩阵法(时间复杂度高,O(N2))以及队列优化法(时间复杂度O(N * M)),

在这里在给出一种写法, 我觉得比较实用, 效率也比较高。

优先队列+链式前向星。

用优先队列的原因是因为拓扑排序出来的结果是不唯一的, 假如题目要求要从小到大或者从大到小都可以直接控制。
 
HDU上用C++会CE错误, 要用G ++提交。
 
#include<stdio.h>
#include<queue>
#include<string.h>
#include<vector>
using namespace std;

int cnt, head[501], n;
int in[501];
priority_queue<int, vector<int>, greater<int> >Q;

struct Edge
{
	int to, next;
}edge[250 * 500];

void add(int u, int v)
{
	edge[++ cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

void topo_sort()
{
	int sum = 0;
	int ans[501];
	for(int i = 1; i <= n; i ++) //入度为0的点入队 
		if(!in[i])
			Q.push(i);
	while(!Q.empty())
	{
		int temp = Q.top();//找到一个入度为0的点temp 
		ans[++ sum] = temp;//存答案 
		Q.pop();
		for(int i = head[temp]; i != 0; i = edge[i].next)
		{
			int to = edge[i].to;
			in[to] --;//与temp为弧尾的点的入度 - 1,相当于在图中将temp点删除 
			if(!in[to])//若temp为弧尾的点的入度变为0,那么入队 
			{
				Q.push(to);
			}
		}
	}
	printf("%d", ans[1]);//按照格式输出 
	for(int i = 2; i <= sum; i ++)
	{
		printf(" %d", ans[i]);
	}
	printf("\n");
}

int main()
{
	int m, a, b;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		cnt = 0;
		while(!Q.empty())
			Q.pop();
		memset(head, 0, sizeof(head));
		memset(in, 0, sizeof(in));
		for(int i = 1; i <= m; i ++)
		{
			scanf("%d%d", &a, &b);
			add(a, b);
			in[b] ++;  //存每个点的入度 
		}
		topo_sort();
	}
	return 0;
}

  

 

       
 
posted @ 2019-04-22 13:55  缘未到  阅读(154)  评论(0编辑  收藏  举报