题目描述
知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。ATM 酒店为小 A 准备了 nn 道菜肴,酒店按照为菜肴预估的质量从高到低给予 11 到 nn 的顺序编号,预估质量最高的菜肴编号为 11。
由于菜肴之间口味搭配的问题,某些菜肴必须在另一些菜肴之前制作,具体的,一共有 m 条形如 i 号菜肴必须先于 jj 号菜肴制作的限制,我们将这样的限制简写为 (i,j)(i,j)。
现在,酒店希望能求出一个最优的菜肴的制作顺序,使得小 A 能尽量先吃到质量高的菜肴:
也就是说,
在满足所有限制的前提下,1 号菜肴尽量优先制作。
在满足所有限制,1 号菜肴尽量优先制作的前提下,2 号菜肴尽量优先制作。
在满足所有限制,1 号和 2 号菜肴尽量优先的前提下,3 号菜肴尽量优先制作。
在满足所有限制,1 号和 2 号和 3 号菜肴尽量优先的前提下,4 号菜肴尽量优先制作。
以此类推。
例 1:共 4 道菜肴,两条限制 (3,1)、(4,1),那么制作顺序是 3,4,1,2。
例 2:共 5 道菜肴,两条限制 (5,2)、(4,3),那么制作顺序是 1,5,2,4,3。
例 1 里,首先考虑 1,因为有限制 (3,1) 和 (4,1),所以只有制作完 3 和 4 后才能制作 1,而根据 3 号又应尽量比 44 号优先,所以当前可确定前三道菜的制作顺序是 3,4,13,4,1;接下来考虑 22,确定最终的制作顺序是 3,4,1,23,4,1,2。
例 2 里,首先制作 1是不违背限制的;接下来考虑 2 时有 (5,2) 的限制,所以接下来先制作 5 再制作 2;接下来考虑 33 时有 (4,3)(4,3) 的限制,所以接下来先制作 44 再制作 33,从而最终的顺序是 1,5,2,4,31,5,2,4,3。现在你需要求出这个最优的菜肴制作顺序。无解输出 Impossible!(首字母大写,其余字母小写)
输入格式
第一行是一个正整数 t,表示数据组数。接下来是 t 组数据。对于每组数据:第一行两个用空格分开的正整数 n 和 mm,分别表示菜肴数目和制作顺序限制的条目数。接下来 mm 行,每行两个正整数 x,yx,y,表示 xx 号菜肴必须先于 yy 号菜肴制作的限制。
输出格式
输出文件仅包含 t 行,每行 n 个整数,表示最优的菜肴制作顺序,或者 Impossible! 表示无解。
输入输出样例
输入 #1复制
3
5 4
5 4
5 3
4 2
3 2
3 3
1 2
2 3
3 1
5 2
5 2
4 3
输出 #1复制
1 5 3 4 2
Impossible!
1 5 2 4 3
解析:
最佳的序列并不是最小字典序。有第3个样例可以知道。
如果用一个小根堆来维护拓扑的话显然是会不行的,因为这样求出来的是字典序最小的拓扑序,并不一定是1尽可能在前
因为字典序是贪心的,如果前面的一位能小就尽可能的小,并不保证1出现尽量靠前
但是如果建一个反图,求一个反向字典序最大的拓扑序呢
那么就会有大的数尽量靠前的情况出现,于是交小的数尽量靠后,于是反过来就是小的数尽量靠前了
于是反着建图+一个大根堆维护就好了
#include<bits/stdc++.h>
using namespace std;
const int MAX = 100010;
struct edge {
int v, next;
}e[MAX];
int d, n, m, in[MAX], cnt, head[MAX], a[MAX];
priority_queue<int> q; //默认大根堆
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c>'9') {
if (c == '-') {
f = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + c - '0';
c = getchar();
}
return x * f;
}
void add(int u, int v) {
e[++cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt;
}
int bfs() {
while (!q.empty()) {
int p = q.top();
q.pop();
cnt++;
a[cnt] = p;
for (int i = head[p]; i > 0; i = e[i].next) {
int v = e[i].v;
in[v]--;
if (!in[v]) {
q.push(v);
}
}
}
return cnt == n;//没有环
}
int main() {
d = read();
while (d--) {
memset(in, 0, sizeof(in));
memset(head, 0, sizeof(head));
cnt = 0;
n = read(), m = read();
for (int i = 1; i <= m; i++) {
int x, y;
x = read(), y = read();
add(y, x); // 反向边
in[x]++;
}
for (int i = 1; i <= n; i++) {
if (!in[i]) {
q.push(i);
}
}
cnt = 0;
if (bfs()) {
for (int i = n; i >= 1; i--) {
printf("%d ", a[i]);
}
printf("\n");
}
else {
printf("Impossible!\n");
}
}
return 0;
}