P3243 【HNOI2015】料理作り(思考+位相的分類+逆グラフ)

トピックの説明
有名な美食家が料理を評価するために ATM ホテルに招待されます。ATM ホテルは A さんのために nn 個の料理を用意しました。ホテルは料理の推定品質に応じて 11 から nn までの番号を割り当て、推定品質が最も高い料理の数は 11 です。

料理の味合わせの問題から、他の料理より先に作らなければならない料理があります。具体的には、料理iを料理jjより先に作らなければならないという合計m個の制限があります。このような制限を( i,j )と略します。 (i,j)。

さて、ホテルは、A 君が最初に質の高い料理を食べられるように、料理を作る最適な順序を見つけたいと考えています。

つまり、

すべての制約を満たすことを前提に、料理No.1をできるだけ最初に作る必要があります。

全ての制約を満たし、できるだけ1番の料理を作るという前提で、できるだけ2番の料理を作ります。

すべての制約が満たされ、料理No.1と料理No.2が可能な限り優先されるという前提の下、料理No.3は可能な限り準備されるべきである。

すべての制約が満たされ、料理 No.1、No.2、No.3 が可能な限り優先されるという前提の下では、料理 No.4 が可能な限り作られるべきです。

等々。

例 1: 合計 4 つの料理、2 つの制限 (3,1)、(4,1) の場合、生産順序は 3,4,1,2 となります。

例 2: 合計 5 つの料理があり、制限が (5,2)、(4,3) の 2 つある場合、生産順序は 1,5,2,4,3 となります。

例 1 では、最初に 1 を検討します。制約 (3,1) と (4,1) があるため、1 は 3 と 4 が作成された後にのみ作成できます。また、数字 3 によれば、44 よりも 3 が優先される必要があります。最初の 3 つの皿の製造順序が 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 になります。次に、料理を作る最適な順序を見つける必要があります。解決策が出力されません 不可能! (最初の文字は大文字、残りの文字は小文字)

入力形式
最初の行は正の整数 t で、データ セットの数を示します。次は t グループのデータです。データの各セットについて: 最初の行には、スペースで区切られた 2 つの正の整数 n と mm が含まれます。これらはそれぞれ、生産順序によって制限される皿の数とエントリの数を表します。次の mm 行には、各行に 2 つの正の整数 x、yx、y が含まれており、料理 xx は料理 yy の前に作成する必要があるという制限を示しています。

出力形式
出力ファイルには t 行のみが含まれ、各行には n 個の整数が含まれており、これは最適な調理シーケンスを意味し、不可能! は解決策がないことを意味します。

入力 出力サンプル
入力 #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 不可能!  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