构造 \(X_i=\sum_{j=1}^m (-1)^{i+j}A_{i,j}\)\(Y_j=\sum_{i=1}^n (-1)^{i+j}A_{i,j}\)

则一个矩阵 \(B\) 能被矩阵 \(A\) 变成当且仅当 \(\left\{X\right\},\left\{Y\right\}\) 均相等。

必要性:很显然,因为操作是不会导致 \(\left\{X\right\}\)\(\left\{Y\right\}\) 改变的。

充分性:对于 \(i\in [1,n-1],j\in[1,m-1]\),从上往下,从左到右依次调整,而每行的最右端与每列的最下端因为 \(\left\{X\right\},\left\{Y\right\}\) 均相等被唯一确定。

转换问题,变成给定 \(\left\{X\right\},\left\{Y\right\}\),满足 \(\sum X_i=\sum Y_j\),求一个 \(B\) 满足 \(X_i=\sum_{j=1}^m (-1)^{i+j}B_{i,j}\)\(Y_j=\sum_{i=1}^n (-1)^{i+j}B_{i,j}\),最小化 \(\sum \left|B_{i,j}\right|\)

\(B\) 从全零矩阵开始操作,题目变成:

  • 每次操作 \((i,j,k)\),使得 \(X_i,Y_j\) 加上 \(k\),代价为 \(\left|k\right|\),需要让 \(\left\{X\right\},\left\{Y\right\}\) 都变成 \(0\),最小化代价。

显然有个下界是 \(\sum \left|B_{i,j}\right|\ge \max(\sum\left|X_i\right|,\sum\left|Y_j\right|)\),接下来构造一组能取到下界的解。

  • 如果存在 \(X_i,Y_j\gt0\),则操作 \((i,j,-1)\)
  • 如果存在 \(X_i,Y_j\lt0\),则操作 \((i,j,1)\)
  • 如果存在 \(X_i\gt0,X_j\lt0\),则操作 \((i,1,-1),(j,1,1)\)
  • 如果存在 \(Y_i\gt0,Y_j\lt0\),则操作 \((1,i,-1),(1,j,1)\)

容易发现由于 \(\sum X_i=\sum Y_j\),则前两种操作完后,必有 \(\left\{X\right\}\)\(\left\{Y\right\}\) 全为 \(0\),后面两种操作就是对剩下非 \(0\) 的进行处理,每一步都是必要且不浪费的,因此这样能取到下界。

时间复杂度 \(\mathcal O(n^2)\)

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505;
int n, m;
ll ans;
ll b[N][N];
ll X[N], Y[N];

void upd(int x, int y, ll c) {
	X[x] -= c, Y[y] -= c;
	b[x][y] += ((x + y) & 1) ? -c : c;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) {
			ll k; scanf("%lld", &k);
			if ((i + j) & 1) X[i] -= k, Y[j] -= k;
			else X[i] += k, Y[j] += k;
		}
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (X[i] > 0 && Y[j] > 0)
				upd(i, j, min(X[i], Y[j]));
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (X[i] < 0 && Y[j] < 0)
				upd(i, j, max(X[i], Y[j]));
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			if (X[i] > 0 && X[j] < 0) {
				ll tmp = min(X[i], -X[j]);
				upd(i, 1, tmp);
				upd(j, 1, -tmp);
			}
	for (int i = 1; i <= m; ++i)
		for (int j = 1; j <= m; ++j)
			if (Y[i] > 0 && Y[j] < 0) {
				ll tmp = min(Y[i], -Y[j]);
				upd(1, i, tmp);
				upd(1, j, -tmp);
			}
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			ans += abs(b[i][j]);
	printf("%lld\n", ans);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) printf("%lld ", b[i][j]);
		printf("\n");
	}
	return 0;
}

原文地址:http://www.cnblogs.com/Kobe303/p/16876868.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性