私の連絡先情報
郵便メール:
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
この質問は、2009 ICPC アジア地域大会の上海部門からの出題です。
フラワーショーでは、m行n列(m≦n)の長方形の中にn種類の花を配置する方法があり、各行のnマスに配置される花はそれぞれ異なります。各列の m 個のセルも互いに異なります。このルールに従って花の列をもう1行追加し、k番目の合理的な配置案を辞書順に出力したいと思います。k番目の案が存在しない場合は、1≤N≤200,0≤M≤N,1を出力します。 ≤K≤ 200。
二部グラフ + dfs 列挙型枝刈り、アイデアはこの記事から来ていますブログ。
各列に配置できる花の数を見つけ、エッジを接続し、2 部グラフのマッチングを実行して完全に一致するかどうかを確認します。そうでない場合は、配置できないことを意味します。この 2 部グラフのマッチングを実行することは無駄ではありません。これは後で使用されます。
k 番目の辞書順序が必要なので、各列に配置できる花を列挙する必要があります。ここで枝刈りが必要になるのは、現在の列挙列が j 番目の列で、後続の j+1 列から N 番目の列が一致しない場合、それらを枝刈りすることができます。
#include <iostream>
#include <cstring>
using namespace std;
#define N 205
int g[N][N], f[N][N], c[N], px[N], py[N], vis[N], ans[N], p1[N], p2[N], m, n, k, clk; bool use[N];
bool dfs(int u, int pos = -1) {
if (u <= pos) return false;
vis[u] = clk;
for (int i=0, v; i<c[u]; ++i) if (py[v = g[u][i]] < 0 || (vis[py[v]]!=clk && dfs(py[v], pos))) {
px[u] = v; py[v] = u;
return true;
}
return false;
}
int max_match() {
memset(px, -1, sizeof(px)); memset(py, -1, sizeof(py)); memset(vis, -1, sizeof(vis));
int cc = 0;
for (int i=1; i<=n; ++i) if (px[i] < 0 && dfs(clk = i)) ++cc;
return cc;
}
bool ok(int u, int v) {
if (px[u] == v) return true;
int x = 0;
for (int i=1; i<=n; ++i) {
p1[i] = px[i]; p2[i] = py[i];
if (px[i] == v) if ((x = i) < u) return false;
}
px[x] = py[px[u]] = -1; px[u] = v; py[v] = u; ++clk;
if (dfs(x, u)) return true;
for (int i=1; i<=n; ++i) px[i] = p1[i], py[i] = p2[i];
return false;
}
bool find(int u) {
if (u > n) return ++m == k;
for (int i=0, v; i<c[u]; ++i) if (!use[v = g[u][i]] && ok(u, v)) {
use[v] = true; ans[u] = v;
if (find(u+1)) return true;
use[v] = false;
}
return false;
}
void solve () {
cin >> n >> m >> k;
for (int i=1; i<=m; ++i) for (int j=1; j<=n; ++j) cin >> f[i][j];
for (int i=1; i<=n; ++i) {
memset(vis, c[i] = 0, sizeof(vis));
for (int j=1; j<=m; ++j) vis[f[j][i]] = true;
for (int j=1; j<=n; ++j) if (!vis[j]) g[i][c[i]++] = j;
}
if (max_match() != n) {
cout << " -1" << endl;
return;
}
memset(use, m = 0, sizeof(use)); memset(vis, clk = 0, sizeof(vis));
if (find(1)) {
for (int i=1; i<=n; ++i) cout << ' ' << ans[i];
cout << endl;
} else cout << " -1" << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
for (int k=1; k<=t; ++k) cout << "Case #" << k << ':', solve();
return 0;
}