题意:
求一个n的排列,有一些限制
做法:
注意这个不同于求字典序最小。
一个例子是n=4,限制有<3,1>,<2,4>
答案应该是3124
如果是字典序最小是2431
我们发现编号小的尽量在前,相当于将序列反过来以后,字典序要最大。
这个很好理解,因为假如你反着来字典序没有最大,肯定会有一个较小的数在后面。
而事实上是可以将它提前的。
所以原问题转化成了反向建图,求字典序最大的拓扑序。用一个堆维护即可。
代码:
/*************************************************************
Problem: bzoj 4010 [HNOI2015]菜肴制作
User: fengyuan
Language: C++
Result: Accepted
Time: 1012 ms
Memory: 3756 kb
Submit_Time: 2017-12-27 17:41:50
*************************************************************/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 100010;
int n, m, cnt, tot;
int head[N], in[N], ans[N];
struct Edge{ int to, nex; }e[N];
inline void add(int x, int y)
{
e[++ cnt].to = y;
e[cnt].nex = head[x];
head[x] = cnt;
}
inline void tp()
{
priority_queue<int> q; tot = 0;
for(int i = 1; i <= n; i ++)
if(!in[i]) q.push(i);
while(!q.empty()) {
int u = q.top(); q.pop(); ans[++ tot] = u;
for(int i = head[u]; i; i = e[i].nex) {
int v = e[i].to; in[v] --;
if(!in[v]) q.push(v);
}
}
if(tot != n) { puts("Impossible!"); return; }
for(int i = tot; i >= 1; i --) printf("%d ", ans[i]); puts("");
}
int main()
{
int Test; scanf("%d", &Test);
while(Test --) {
scanf("%d%d", &n, &m);
memset(in, 0, sizeof in);
cnt = 0; memset(head, 0, sizeof head);
for(int i = 1; i <= m; i ++) {
int x, y; scanf("%d%d", &x, &y);
add(y, x); in[x] ++;
}
tp();
}
return 0;
}