拓扑排序--逃生

逃生

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

现在有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

测试样例分析:

 

有个坑:

/*
*	这题反向存储就对,正向存储可以通过测试案例,但是wa.
*	之后查了下:(dalao的原话..)
*	 	明显的拓扑排序,但是这一题的条件有两个,除了给的限定条件外还应该优先按照从1~n输出。
*		所以考虑使用优先队列来取合适的点考虑前面的应该尽量比后面的大,所以若用优先队列,则
*		若正向建边,会导致队列中已有的上一层元素比后推入的元素大,从而使队列完全按照从大到
*		小输出,产生错误结果。所以考虑反向建边,则先取最一小级中最大的,并且每一次取当前队
*		列中的最大的,则可以保证在按照1~n的顺序的情况下,按照特殊条件排序。
*/
#include<stdio.h>
#include<cstring>
#include<queue>
#define MAX 30000+1

using namespace std;

int in[MAX];//存放各点的入度 
vector<int> v[MAX],ans;
//v[MAX]用来记录有关系的元素相当于二维数组; 
//ans数组用来存放最终序列 
int n,m,t;

void toposort(){
	priority_queue<int> q;//默认从大到小排序 
	//priority_queue<int,vector<int>,greater<int> > q; //从小到大排序 
	for(int i=1;i<=n;i++)
		if(in[i]==0)
		//先把入度为0的点找出来入队列;采用优先队列来存取出度为0的的点
			q.push(i); 
		
	while(!q.empty()){
		int a=q.top(); q.pop(); //从优先队列中取出最小的入度为零的点 
		ans.push_back(a);//按照先后顺序放入ans数组中; 
		
		for(int j=0;j<v[a].size();j++){
			int b = v[a][j]; //v[a]中保存的是a-->j的j 
			in[b]--;
			if(in[b]==0)//在这过程中,若某点的入度变为0,则入队 
			  q.push(b);
		}
	}
}

int main(){
	scanf("%d",&t);//t组测试样例 
	while(t--){
		scanf("%d%d",&n,&m);//n个人,m个约束条件 
		
		//初始化
		memset(in,0,sizeof(in));
		ans.clear();
		for(int i=1;i<=n;i++){
			v[i].clear();
		}
		
		for(int i=1;i<=m;i++){
			int a,b;
			scanf("%d%d",&a,&b); //反向建立关系 
			in[a]++; //a <-- b 
			/*
			in[b]++; //a --> b
			v[a].push_back(b);//将a所指向的元素都压入v[a]中
			*/
			v[b].push_back(a);//把与b有关系的元素存到edge[b]中;
		}
		
		toposort();
		/*
		for(int i = 0;i<ans.size();i++){
			if(i!=ans.size()-1)
				printf("%d ",ans[i]);
			else
				printf("%d\n",ans[i]);
		}
		*/
		for(int i=ans.size()-1;i>=0;i--)//反向输出; 
		{
			if(i!=0)
			  printf("%d ",ans[i]);
			else
			  printf("%d\n",ans[i]);
		}	
	} 
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值