[ABC317G] Rearranging 题解

发布时间 2023-08-27 13:54:53作者: SunnyYuan

取自我的洛谷博客:https://www.luogu.com.cn/blog/SunnyYuan/solution-at-abc317-g

借鉴了官方题解思路。

思路

首先我们要建立一个二分图。

对于输入的 \(a_{i, j}\),我们可以连接 左侧的 \(i\) 和 右侧的 \(a_{i, j}\)

比如样例 \(1\)

注意:左边的 \(1, 2, 3\) 和 右边的 \(1, 2, 3\) 完全不一样,一个是行数,一个是数字。

  1. 那我们现在找出一组二分图的最大匹配,那么就代表对于固定的一列,第 \(i\) 行的数字就可以确定了。

    比如上图中橙色的边,它们就是一组二分图的最大匹配,我们可以通过其知道对于一列,可以这么填:

\[\begin{aligned} 1\\ 3\\ 2\\ \end{aligned} \]

  1. 我们将已经匹配的边删去,然后再跑下一次的二分图,构建下一列的数字。就这样执行 \(m\) 遍,就可以做出答案。

    可以得到最大匹配,然后构建出这一列数字:

\[\begin{aligned} 1\\ 2\\ 3\\ \end{aligned} \]

  1. 最后将这么多列数字按任意顺序输出就可以了。

\[\begin{aligned} \text{1 1}\\ \text{2 3}\\ \text{3 2}\\ \end{aligned} \]

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 210, M = 40010, INF = 0x3f3f3f3f;

struct edge {
	int to, next, w;
} e[M];

int head[N], idx = 1;

void add(int u, int v, int w) {
	idx++, e[idx].to = v, e[idx].next = head[u], e[idx].w = w, head[u] = idx;
	idx++, e[idx].to = u, e[idx].next = head[v], e[idx].w = 0, head[v] = idx;
}

int S, T;
int n, m;
int q[N], hh, tt;
int d[N];
int ans[N][N];

bool bfs() {
	memset(d, 0, sizeof(d));
	hh = tt = 0;
	q[0] = S;
	d[S] = 1;

	while (hh <= tt) {
		int u = q[hh++];

		for (int i = head[u]; i; i = e[i].next) {
			int to = e[i].to;
			if ((!d[to]) && e[i].w) {
				d[to] = d[u] + 1;
				q[++tt] = to;
			}
		}
	}

	return d[T];
}

int dinic(int u, int limit) {
	if (u == T) return limit;

	int rest = limit;
	for (int i = head[u]; i && rest; i = e[i].next) {
		int to = e[i].to;
		if (d[to] == d[u] + 1 && e[i].w) {
			int k = dinic(to, min(rest, e[i].w));
			if (!k) d[to] = INF;
			rest -= k;
			e[i].w -= k;
			e[i ^ 1].w += k;
		}
	}
	return limit - rest;
}

int maxflow() {
	int ans = 0, flow = 0;
	while (bfs()) while (flow = dinic(S, INF)) ans += flow;
	return ans;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n >> m;
	S = 0, T = n << 1 | 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			int x;
			cin >> x;
			add(i, x + n, 1);
		}
	}
	int tmp = idx;
	for (int i = 1; i <= n; i++) add(S, i, 1), add(i + n, T, 1);

	for (int j = 1; j <= m; j++) {
		if (maxflow() != n) {
			cout << "No\n";
			return 0;
		}
		for (int i = 3; i <= tmp; i += 2) if (e[i].w == 1) {
			int u = e[i].to, v = e[i ^ 1].to;
			ans[u][j] = v - n;
			e[i].w = e[i ^ 1].w = 0;
		}
		for (int i = tmp + 2; i <= idx; i += 2) {
			if (e[i].w == 1) {
				e[i ^ 1].w = 1;
				e[i].w = 0;
			}
		}
	}
	cout << "Yes\n";
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cout << ans[i][j] << ' ';
		}
		cout << '\n';
	}
	return 0;
}