2018 Multi-University Training Contest 3 05
题意
给定 \(n\ *\ m\) 个数字,可以修改不超过 \(A\) 个数字为 \(0\)。
问选 \(B\) 个不相交的列数为 \(m\) 的矩形所能得到的和的最大值是多少。
\(1\ \leq\ n\ \leq\ 100,\ 1\ \leq\ m\ \leq\ 3000,\ 0\ \leq\ A\ \leq\ 10000,\ 1\ \leq\ B\ \leq\ 3,\ |w_{i,\ j}|\ \leq\ 10^9\)
做法1
令 \(F(i,\ j,\ k,\ 0/1)\) 表示考虑过前 \(i\) 行,改了 \(j\) 个数字,\(k\) 个矩形开始选了,当前行选不选的最大值。
有转移 \(f(i,\ j,\ k,\ 0)\ =\ max(f(i\ -\ 1,\ j,\ k,\ 0),\ f(i\ -\ 1,\ j,\ k,\ 1)),\ f(i,\ j,\ k,\ 1)\ =\ max\{f(i\ -\ 1,\ j\ -\ a,\ k\ -\ 1,\ 0)\ +\ g(i,\ a),\ f(i\ -\ 1,\ j\ -\ a,\ k,\ 1)\ +\ g(i,\ a)\}\)。\(g(i,\ a)\) 为第 \(i\) 行改 \(a\) 个数字的和。
考虑 \(f(i,\ j,\ k,\ 1)\) 的转移,由于 \(g(i,\ a\ +\ 1)\ -\ g(i,\ a)\ \geq\ g(i,\ a\ +\ 2)\ -\ g(i,\ a\ +\ 1)\),所以转移具有单调性,证明如下:
考虑转移 \(f_i\ =\ max\{b_j\ +\ a_{i\ -\ j}\}\),有 \(a_{i\ +\ 1}\ -\ a_i\ \geq\ a_{i\ +\ 2}\ -\ a_{i\ +\ 1}\)。
设 \(l,\ r,\ l\ <\ r,\ b_l\ +\ a_{i\ -\ l}\ <\ b_r\ +\ a_{i\ -\ r}\)。则当 \(i\ +\ 1\) 时,\(b_l\ +\ a_{i\ +\ 1\ -\ l}\ =\ b_l\ +\ a_{i\ -\ l}\ +\ (a_{i\ +\ 1\ -\ l}\ -\ a_{i\ -\ l}),\ b_r\ +\ a_{i\ +\ 1\ -\ r}\ =\ b_r\ +\ a_{i\ -\ r}\ +\ (a_{i\ +\ 1\ -\ r}\ -\ a_{i\ -\ r})\)。因为 \(b_l\ +\ a_{i\ -\ l}\ <\ b_r\ +\ a_{i\ -\ r},\ a_{i\ +\ 1\ -\ l}\ -\ a_{i\ -\ l}\ <\ a_{i\ +\ 1\ -\ r}\ -\ a_{i\ -\ r}\),所以 \(b_l\ +\ a_{i\ +\ 1\ -\ l}\ <\ b_r\ +\ a_{i\ +\ 1\ -\ r}\)。
直接分治转移即可。
代码
#include <bits/stdc++.h>
#ifdef DEBUG
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
const int maxn = 110, maxm = 3010, maxa = 1e4 + 10, maxb = 4;
const long long oo = 1ll << 60;
int n, m, A, B;
long long f[maxb][2][maxa], g[maxm], h[maxa], t[maxb][2][maxa];
void trans(int l, int r, long long *f, int bl, int br, long long *h) {
int mid = l + r >> 1, rem = bl;
long long x = -oo;
for (int i = bl; i <= br; ++i) {
if(i > mid) break;
int j = mid - i;
if(j <= m) {
long long y = h[i] + g[j];
if(y > x) x = y, rem = i;
}
}
f[mid] = max(f[mid], x);
if(l < mid) trans(l, mid - 1, f, bl, rem, h);
if(mid < r) trans(mid + 1, r, f, rem, br, h);
return;
}
void solve() {
scanf("%d%d%d%d", &n, &m, &A, &B);
for (int k = 0; k <= B; ++k) for (int l = 0; l < 2; ++l) for (int j = 0; j <= A; ++j) f[k][l][j] = -oo;
f[0][0][0] = 0;
for (int i = 1; i <= n; ++i) {
static int foo[maxm];
g[0] = 0;
for (int j = 1; j <= m; ++j) scanf("%d", foo + j), g[0] += foo[j];
sort(foo + 1, foo + m + 1);
for (int j = 1; j <= m; ++j) g[j] = g[j - 1] - foo[j];
for (int k = 0; k <= B; ++k) for (int l = 0; l < 2; ++l) for (int j = 0; j <= A; ++j) t[k][l][j] = -oo;
for (int k = 0; k <= B; ++k) {
for (int j = 0; j <= A; ++j) {
t[k][0][j] = max(f[k][0][j], f[k][1][j]);
if(k) h[j] = max(f[k - 1][0][j], f[k][1][j]);
else h[j] = f[k][1][j];
}
trans(0, A, t[k][1], 0, A, h);
}
for (int k = 0; k <= B; ++k) for (int l = 0; l < 2; ++l) for (int j = 0; j <= A; ++j) f[k][l][j] = t[k][l][j];
}
long long ans = -oo;
for (int k = 0; k <= B; ++k) for (int l = 0; l < 2; ++l) for (int j = 0; j <= A; ++j) ans = max(ans, f[k][l][j]);
printf("%"LLFORMAT"d\n", ans);
return;
}
int main() {
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}