GitBook

逃生

Problem Description
糟糕的事情发生啦,现在大家都忙着逃命。但是逃命的通道很窄,大家只能排成一行。

现在有n个人,从1标号到n。同时有一些奇怪的约束条件,每个都形如:a必须在b之前。
同时,社会是不平等的,这些人有的穷有的富。1号最富,2号第二富,以此类推。有钱人就贿赂负责人,所以他们有一些好处。

负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推。

那么你就要安排大家的顺序。我们保证一定有解。 

Input
第一行一个整数T(1 <= T <= 5),表示测试数据的个数。
然后对于每个测试数据,第一行有两个整数n(1 <= n <= 30000)和m(1 <= m <= 100000),分别表示人数和约束的个数。

然后m行,每行两个整数a和b,表示有一个约束a号必须在b号之前。a和b必然不同。
 
Output
对每个测试数据,输出一行排队的顺序,用空格隔开。
 
Sample Input
1 5 10 3 5 1 4 2 5 1 2 3 4 1 4 2 3 1 5 3 5 1 2
 
Sample Output
1 2 3 4 5

明显的拓扑排序,但是这一题的条件有两个,除了给的限定条件外还应该优先按照从1~n输出。

所以考虑使用优先队列来取合适的点

考虑前面的应该尽量比后面的大,所以若用优先队列,则若正向建边,会导致队列中已有的上一层元素比后推入的元素大,从而使队列完全按照从大到小输出,产生错误结果。

所以考虑反向建边,则先取最一小级中最大的,并且每一次取当前队列中的最大的,则可以保证在按照1~n的顺序的情况下,按照特殊条件排序。

因为是反向建边,所以要把得到的结果倒序输出。

测试数据:

10
6 4
6 3
3 1
5 4
4 2

输出:

6 3 1 5 4 2


#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n;
int cnt[30200];
int topo[30200];
struct node {
	int next;
	int to;
} a[100200];
int head[30200];
void toposort() {
	priority_queue<int>q;
	for(int i=1; i<=n; i++)
		if(!cnt[i]) {
			q.push(i);
		}
	int t=0;
	while(!q.empty()) {
		int ttop=q.top();
		q.pop();
		topo[t++]=ttop;
		for(int i=head[ttop]; i!=-1; i=a[i].next) {
			cnt[a[i].to]--;
			if(!cnt[a[i].to])
				q.push(a[i].to);
		}
	}
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		int m;
		scanf("%d %d",&n,&m);
		memset(cnt,0,sizeof(cnt));
		memset(head,-1,sizeof(head));
		for(int i=0; i<m; i++) {
			int u,v;
			scanf("%d %d",&u,&v);
			a[i].to=u;
			a[i].next=head[v];
			head[v]=i;
			cnt[u]++;
		}
		toposort();
		for(int i=n-1; i>=0; i--)
			printf("%d%c",topo[i],i==0?'\n':' ');
	}
	return 0;
}


题目地址:【杭电】[4857]逃生