トピックの説明
有名な美食家が料理を評価するために 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;
}