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;
}
posted @ 2018-09-12 11:39  King_George  阅读(154)  评论(0编辑  收藏  举报