P3243 [HNOI2015]菜肴制作(思维 + 拓扑排序+反图)

题目描述
知名美食家小 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;
}

猜你喜欢

转载自blog.csdn.net/zhi6fui/article/details/129648937