【NOI2022省选挑战赛 Contest3 A】矩阵(KM算法)

矩阵

题目链接:NOI2022省选挑战赛 Contest3 A

题目大意

给你一个 n*n 的矩阵 A,然后要你构造一个矩阵 B 使得对于每个位置 B 的值都不小于 A 的,并且 B 满足 Bi+1,j+1+Bi,j=Bi+1,j+Bi,j+1

思路

考虑移项:\(B_{i+1,j+1}=B_{i,j+1}+B_{i+1,j}-B_{i,j}\)
然后它每次就可以弄成一个阶梯状,然后依次是 \(1,-1,1,-1,1\)
然后你每次再把中间的 \(1\) 点掉,你会发现把会变成一个只有三个 \(1\),而且三个 \(1\) 构成等腰三角形的感觉。

然后我们考虑把它移动到直角的点在 \((0,0)\),然后我们设 \(B_{0,0}=0\),那我们就是要构造 \(B_{i,0},B_{0,i}\),使得 \(B_{i,0}+B_{0,j}\geqslant B_{i,j}\) 且最小化所有 \(B_{i,j}\) 的和。

你会想到什么?KM算法!
然后你直接上跑最大权完美匹配就好了。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f

using namespace std;

int n, m, a[51][51], pre[51];
int ex[51], ey[51], matched[51], slack[51];
bool iny[51];

void match(int now) {
	memset(iny, 0, sizeof(iny));
	memset(pre, 0, sizeof(pre));
	for (int i = 1; i <= n; i++) slack[i] = INF;
	int x, y = 0, ty = 0;
	matched[y] = now;
	while (1) {
		x = matched[y]; iny[y] = 1; int d = INF;
		for (int i = 1; i <= n; i++) {
			if (iny[i]) continue;
			if (slack[i] > ex[x] + ey[i] - a[x][i]) {
				slack[i] = ex[x] + ey[i] - a[x][i];
				pre[i] = y;
			}
			if (slack[i] < d) {
				d = slack[i]; ty = i;
			}
		}
		for (int i = 0; i <= n; i++) {
			if (iny[i]) ex[matched[i]] -= d, ey[i] += d;
				else slack[i] -= d;
		}
		y = ty; if (!matched[y]) break;
	}
	while (y) {
		matched[y] = matched[pre[y]];
		y = pre[y];
	}
}

int KM() {
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			ex[i] = max(ex[i], a[i][j]);
	for (int i = 1; i <= n; i++) {
		match(i);
	}
	int re = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			re += ex[i] + ey[j];
	return re;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			scanf("%d", &a[i][j]);
	
	printf("%d\n", KM());
	if (!m) return 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			printf("%d ", ex[i] + ey[j]);
		}
		printf("\n");
	}
	
	return 0;
}
posted @ 2022-02-28 11:04  あおいSakura  阅读(83)  评论(0编辑  收藏  举报