【vj】给定一些先后约束,求原序列(拓扑排序)

本文讨论了一个关于人员排序的问题,其中涉及到约束条件和优先级队列的使用。通过对约束条件的处理和优先级队列的巧妙应用,实现了一种有效的排序策略。文章深入分析了排序过程中的关键步骤,包括约束链的构建、优先级队列的构建与更新,以及最终输出符合约束条件的排序序列。通过实例展示了该方法的有效性和实用性。
摘要由CSDN通过智能技术生成

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

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

http://vj.hsacm.com/contest/view.action?cid=67#problem/A

其实我觉得蛮有难度的,深搜,链表都试过了,都是写到结尾被否决掉。归根结底是我思路不行。

说说我一开始的思路:先把先后的链构出来,然后发现会有很多这样平行不相关的链,不知道怎么交叉输出。然后想到用拓扑思想逐个变成符合条件输出,但是可能会出现前面遍历过的点因为后面的点删掉而变符合,然后又可以删。然而是继续往下遍历还是找到前面的点深层遍历我没搞清楚(其实这里有个因果关系)。然后我又想到用深搜解决这个问题,然而这样就不能交错输出了。继而又想到了链表。然而写到后来发现进行不下去了。。。

其实我的理解也有错误。应该是以编号优先(比如说4 1/4 1应该输出4 1 2 3而不是2 3 4 1),由这个例子可以知道应该先以编号优先再以约束优先。

正确思路是拓扑+优先队列。这里还有个要拐弯的地方是,队列里的东西在最后输出的时候是反向的(不知道是怎么利用这一点想到思路的,那就让我废话一下来梳理思路好了。。。)

首先拿到很多约束(a在b前),而没有受到约束的点(包括约束放后的点)是可以随意放置的,而有约束的点却必须放在某一些点的前面。那不如把能放后面的点先放后面,这样也能贪心地保证可以满足较多的情况(为什么不正推?因为先以编号优先再以约束优先 )。那么什么样的点适合放后面?肯定是没有约束的点从小到大放后面。那么,当前没有约束的候选点里的最大点一定是确定放最后一个的,而这个点放完了又会导致一些与它约束的点变成了没有约束的点,又成了放后面的候选点,再从候选点里面选一个进行同样的操作。

所以这样子最后输出的点就会变成逆序的了。

#include <bits/stdc++.h>  
using namespace std;  
typedef long long ll;
vector<int> x[30005];
vector<int> v;
int y[30005];
struct cmp{
   bool operator()(const int &t1,const int &t2){
        return t1<t2;  //从大到小,与数组规则相反 
   }
};
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		priority_queue<int,vector<int>,cmp> q;
		memset(y,0,sizeof(y)); 
		v.clear();
		int n,m,a,b;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i)
			x[i].clear();
		while(m--){
			scanf("%d%d",&a,&b);
			x[b].push_back(a);
			y[a]++;
		}
		for(int i=1;i<=n;++i){
			if(y[i]==0){
				q.push(i);
			}
		}
		while(!q.empty()){
			int w=q.top();
			v.push_back(q.top());
			q.pop();
			for(int i=0;i<x[w].size();++i){
				y[x[w][i]]--;
				if(y[x[w][i]]==0){
					q.push(x[w][i]);
				}
			}
		}
		for(int i=v.size()-1;i>0;--i)
			printf("%d ",v[i]);
		printf("%d\n",v.front());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值