题目链接:
解题思路:
看到题目,可以想到拓扑排序。但是如果要求字典序最小的排列,那就错了。
可以举出反例:4种菜肴,限制为<2,4><3,1>,
那么字典序最小的是2,3,1,4,但题目要求的最优解是3,1,2,4。
继续考虑,可以发现,如果最后一个数字在合法范围内尽可能大,那么这样是绝对有利的。
因为如果设最后一个数字是x,那么除了x之外的所有数都不会被放到最后一个位置。
而这样就可以让前面所有小于x的数都尽量靠前(大于x的数,虽然也能靠前,但由于x的位置已经固定,因此没有用),达到题目的目标。
因此,最优解就是符合条件的排列中,反序列的字典序最大的排列。
所以,在反图上跑拓扑排序,求最大字典序。在实现上,由于需要多次找出队列中的最大值,因此用堆代替队列。
代码如下:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 100010, M = 100010; int n, m; int din[N]; int ans[N], cnt; int h[N], e[M], ne[M], idx; void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx ++ ; } bool topsort() { priority_queue<int> q; for (int i = 1; i <= n; i ++ ) if (!din[i]) q.push(i); int sum = 0; while (q.size()) { int t = q.top(); q.pop(); ans[cnt ++ ] = t; for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (-- din[j] == 0) q.push(j); } } if (cnt == n) return true;//说明所有点都入队,没有环 return false; } int main() { int T; cin >> T; while (T -- ) { idx = 0; cnt = 0; memset(h, -1, sizeof h); memset(din, 0, sizeof din); cin >> n >> m; bool is_true = true; for (int i = 0; i < m; i ++ ) { int a, b; scanf("%d %d", &a, &b); add(b, a);//建立反向边 if (a == b) is_true = false; din[a] ++ ; } if (!is_true) { puts("Impossible!"); return 0; } if (topsort()) { for (int i = cnt - 1; i >= 0; i -- ) printf("%d ", ans[i]);//因为时逆向建边,所以逆向输出 puts(""); } else puts("Impossible!"); } return 0; }