Atcoder-ABC317G-Rearranging
ABC317G - Rearranging
题意
给出一个 \(n\) 行 \(m\) 列的矩阵,可以将每一行的 \(m\) 个数重新排列,问能不能得到 \(m\) 列都是 \(n\) 的排列的矩阵,能得到则输出任意一个方案。
给出的矩阵满足对于 \(i \in [1,n]\) ,都恰好出现了 \(m\) 次。
思路
前置知识:霍尔定理(Hall's Threorem),二分图完美匹配定义。
首先,没有无解的情况(亲测将 "NO" 改为 "This_is_FFT" 也能过)。
考虑如何证明这件事。
对于任意 \(n\) 行 \(1\) 列的矩阵,可以得到一列 \(n\) 的排列,有解。
假设任意 \(n\) 行 \(m-1\) 列的矩阵有解。
考虑任意一个 \(n\) 行 \(m\) 列的矩阵。
考虑对于第 \(i\) 行的所有数 \(a_{i,j}\) 建边 \(i \to a_{i,j}\)。这时整张图构成了二分图,左边是行,点集记为 \(V\),右边是行内具体的数,图中共有 \(nm\) 条边。同时记点 \(i\) 指向的点构成的集合为 \(T_i\)。对于这张图,每个点的度数都为 \(m\)。
霍尔定理说明:若二分图满足 \(\forall V' \subseteq V : |\bigcup_{i\in V'} T_i| \ge |V'|\),则原二分图一定存在完美匹配。
而对于原图中选出 \(V' \subseteq V\),记 \(k=|V'|\),则 \(\sum_{i \in V'}|T_i| = mk\),由于对于这些 \(T_i\) 中的任意数最多会有 \(m\) 个相同的数,也就是说互不相同的点的个数一定大于等于 \(k\),即 \(V' \subseteq V : |\bigcup_{i\in V'} T_i| \ge |V'|\),而因为 \(V'\) 可以任取,所以原图满足霍尔定理,故原图有一组完美匹配。
这组完美匹配可以得到一列 \(n\) 的排列,因为左部和右部在完美匹配中行和数一一对应。然后我们将这一列删去,同时在图中删去这组完美匹配,原矩阵变为一个 \(n(m-1)\) 的矩阵。因为 \(n(m-1)\) 矩阵有解,所以可将我们这组完美匹配得到的列整列并在 \(n(m-1)\) 矩阵的答案中,得到 \(nm\) 矩阵的答案。
综上,\(n\) 行 \(m\) 列,且满足对于 \(i \in [1,n]\) ,都恰好出现了 \(m\) 次的矩阵,一定有解。
我们从证明中可以发现,我们每次对于当前矩阵建图,找出一组完美匹配,并入答案并删去后得到新矩阵,这样肯定可以得到一组解。
对于找完美匹配,可以用最大流算法,也可以跑匈牙利算法。
Code
#include <bits/stdc++.h>
using namespace std;
const int inf = 1e7;
int n, m, a[105][105], Ans[105][105], S, T;
int firs[405], nex[40005], to[40005], w[40005], tot = 1, lq, rq;
void Add (int u, int v, int p){
++ tot;
nex[tot] = firs[u];
firs[u] = tot;
to[tot] = v;
w[tot] = p;
++ tot;
nex[tot] = firs[v];
firs[v] = tot;
to[tot] = u;
w[tot] = 0;
}
int cur[405], dep[405], Ansflow;
queue < int > Q;
bool inq[405];
bool Bfs (){
for (int i = 1;i <= T;++ i){
cur[i] = firs[i];
dep[i] = inf;
inq[i] = false;
}
dep[S] = 0;
Q.push (S);
inq[S] = true;
while (! Q.empty ()){
int u = Q.front ();
Q.pop ();
inq[u] = false;
for (int e = firs[u];e;e = nex[e]){
int v = to[e];
if (dep[v] > dep[u] + 1 && w[e] > 0){
dep[v] = dep[u] + 1;
if (! inq[v])
Q.push (v);
inq[v] = true;
}
}
}
return dep[T] != inf;
}
int Solve (int u, int flow){
if (u == T){
Ansflow += flow;
return flow;
}
int used = 0, rlow;
for (int e = cur[u];e;e = nex[e]){
cur[u] = e;
int v = to[e];
if (dep[v] == dep[u] + 1 && w[e] > 0){
rlow = Solve (v, min (flow - used, w[e]));
if (rlow != 0){
used += rlow;
w[e] -= rlow;
w[e ^ 1] += rlow;
if (used == flow)
break;
}
}
}
return used;
}
void Dinic (){
Ansflow = 0;
while (Bfs ())
Solve (S, inf);
}
int main (){
scanf ("%d%d", &n, &m);
S = n << 1 | 1;
T = S + 1;
for (int i = 1;i <= n;++ i)
Add (S, i, 1);
lq = tot;
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= m;++ j){
scanf ("%d", &a[i][j]);
Add (i, n + a[i][j], 1);
}
rq = tot;
for (int i = 1;i <= n;++ i)
Add (n + i, T, 1);
for (int i = 1;i <= m;++ i){
Dinic ();
if (Ansflow != n){
// puts ("No");
puts ("This_is_FFT");
return 0;
}
for (int e = 2;e <= lq;e += 2){
w[e] = 1;
w[e ^ 1] = 0;
}
for (int e = rq + 1;e <= tot;e += 2){
w[e] = 1;
w[e ^ 1] = 0;
}
for (int e = lq + 1;e <= rq;++ e)
if (w[e] > 0 && (e & 1)){
Ans[to[e]][i] = to[e ^ 1] - n;
w[e] = 0;
}
}
puts ("Yes");
for (int i = 1;i <= n;++ i){
for (int j = 1;j <= m;++ j)
printf ("%d ", Ans[i][j]);
puts ("");
}
return 0;
}
本文作者:saubguiu
本文链接:https://www.cnblogs.com/imcaigou/p/17884366.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步