bzoj4010 [HNOI2015]菜肴制作

bzoj4010 [HNOI2015]菜肴制作

求出一个 \(n\) 的排列满足 \(m\) 个形如 \((x,\ y)\) 的限定条件,表示 \(x\) 必须在 \(y\) 之前出现,且使得在满足所有限定条件的情况下, \(1\) 尽量优先,满足 \(1\) 尽量优先的情况下 \(2\) 尽量优先……

\(n,\ m\leq10^5\)

图论,拓扑排序,贪心


原题所求显然与最小字典序有差异……

贪心,为了让编号小的尽量往前排,则编号大的要尽量往后排,这样原题就变成了 在满足限定条件的情况下求反序列的最大字典序

把限定条件看做边 \((y,\ x)\) ,求排列无非是在图中跑一遍拓扑排序,满足最大字典序只需用堆维护

这就是所谓的正难则反吧……

时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
int Tests;
int n, m, now, cnt, h[maxn], deg[maxn], ans[maxn];

struct edges {
  int nxt, to;

  edges(int x = 0, int y = 0) :
    nxt(x), to(y) {}
} e[maxn];

priority_queue <int> Q;

void addline(int u, int v) {
  e[++cnt] = edges(h[u], v), h[u] = cnt;
}

void solve() {
  cnt = now = 0;
  memset(h, 0, sizeof h);
  memset(deg, 0, sizeof deg);
  while (!Q.empty()) Q.pop();
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= m; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    addline(y, x), deg[x]++;
  }
  for (int i = 1; i <= n; i++) {
    if (!deg[i]) Q.push(i);
  }
  while (!Q.empty()) {
    int u = Q.top();
    ans[++now] = u, Q.pop();
    for (int i = h[u]; i; i = e[i].nxt) {
      int v = e[i].to;
      if (!--deg[v]) {
        Q.push(v);
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    if (deg[i] > 0) {
      puts("Impossible!");
      return;
    }
  }
  for (int i = n; i; i--) {
    printf("%d ", ans[i]);
  }
  putchar(10);
}

int main() {
  scanf("%d", &Tests);
  while (Tests--) {
    solve();
  }
  return 0;
}
posted @ 2019-04-30 17:47  cnJuanzhang  阅读(140)  评论(0编辑  收藏  举报