【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;
}