HDU-4857-逃生
题目链接 http://hdu.hustoj.com/showproblem.php?pid=4857
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
对每个测试数据,输出一行排队的顺序,用空格隔开。
逆向拓扑排序+优先队列
这题要求编号小的尽量在前面,要注意与字典序不同。
样例给的不经典,可以看这样一个例子:
如图,用优先队列拓扑排序得到的是 3 5 6 4 1 7 8 9 2 0,但正确答案应该是 6 4 1 3 9 2 5 7 8 0。
在错误的答案中,编号小的(1)显然不在编号大的(2)前面。不满足数值小的尽量排在前面的要求。
我们发现,每一次都取入度为0的并且最小的点输出不能使得最小的点排在尽可能靠前的位置,也就是局部最优不能使得全局最优。
这里我们可以得到“前面的小的不一定排在前面,但是有一点后面大的一定排在后面”。
所以我们要做的就是:反向建图+逆序输出。
建立一个反图,跑一遍字典序最大的拓扑排序,然后倒过来输出就可。
啊对,用 cin和cout 就会 Time Limit Exceeded 。好几遍了。。。。。。
放AC代码
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 #include <queue> 6 #define maxn 30005 7 using namespace std; 8 9 int t,n,m; 10 int in[maxn]; 11 vector<int>edge[maxn]; 12 13 int main() 14 { 15 scanf("%d",&t); 16 while(t--) 17 { 18 priority_queue<int>q; 19 memset(in,0,sizeof(in)); 20 vector<int>res; 21 int u,v; 22 23 scanf("%d%d",&n,&m); 24 for(register int i=1; i<=n; i++) 25 edge[i].clear(); 26 for(register int i=1; i<=m; i++) 27 {//反向建图 28 scanf("%d%d",&u,&v); 29 in[u]++; 30 edge[v].push_back(u); 31 } 32 for(register int i=1; i<=n; i++) 33 {//将反图中入度为0的点加入队列 34 if(in[i]==0) q.push(i); 35 } 36 while(!q.empty()) 37 { 38 u=q.top(); 39 res.push_back(u); 40 q.pop(); 41 for(register int i=0; i<edge[u].size(); i++) 42 { 43 v=edge[u][i]; 44 in[v]--; 45 if(in[v]==0) q.push(v); 46 } 47 } 48 for(register int i=res.size()-1; i>=0; i--) 49 { 50 if(i==res.size()-1) printf("%d",res[i]); 51 else printf(" %d",res[i]); 52 } 53 puts(""); 54 } 55 return 0; 56 }