orz DP 。
Address
https://www.lydsy.com/JudgeOnline/problem.php?id=3111
Solution
画个图可以发现是长城一样的形状。具体是:
2K+1
2
K
+
1
个非空矩形顺次横向相接,这些矩形底部所处的行号相等。
对于第
i(i>1)
i
(
i
>
1
)
,
如果
i
i
是奇数,那么第 个矩形严格高于第
i−1
i
−
1
个矩形。
否则
i
i
是偶数,那么第 个矩形严格低于第
i−1
i
−
1
个矩形。
枚举封闭图形最后一行所处的行号(记为
rw
r
w
):
定义状态:
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
表示由
k
k
个矩形组成,第 个矩形的右下角为
(rw,i)
(
r
w
,
i
)
,高度为
j
j
,前 个矩形的最大权值和。
注:
sum(l,r,i)
s
u
m
(
l
,
r
,
i
)
表示第
i
i
列从第 个格子到第
r
r
个格子的权值之和,可以用前缀和 求出。
转移当然是从
(rw,i−1)
(
r
w
,
i
−
1
)
转移到
(rw,i)
(
r
w
,
i
)
:
可以发现,上面的转移忽略了一种情况:第 k k 个矩形只占用一列。
所以,当 为奇数时:
否则 i i 为偶数时:
上面两个转移在实现上可以用 前缀 / 后缀最大值 将转移优化到 O(1) O ( 1 ) 。
边界比较复杂:
其余初始值为 −∞ − ∞ 。
第一个边界比较好理解,关键是第二个边界中为什么第二维可以为 0 0 。
答案是:当第一个矩形的高度为 时,该状态不可能从 f[...][1][0] f [ . . . ] [ 1 ] [ 0 ] 转移。 (因为高度要 严格大于前一个矩形)
行号上界为 rw r w 时的最优解为:
复杂度 O(n2mK) O ( n 2 m K ) 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 102, M = 24, INF = 0x3f3f3f3f;
int n, m, K, a[N][N], sum[N][N], f[N][N][M], g[N][N][M], h[N][N][M], ans = -INF;
int solve(int rw) {
int i, j, k; For (i, 1, m) sum[rw][i] = sum[rw - 1][i] + a[rw][i];
For (i, 0, m) For (j, 0, rw + 1) For (k, 0, K)
f[i][j][k] = g[i][j][k] = h[i][j][k] = -INF;
For (j, 1, rw) f[0][j][0] = 0, g[0][j][0] = max(g[0][j - 1][0], f[0][j][0]);
Rof (j, rw, 1) h[0][j][0] = max(h[0][j + 1][0], f[0][j][0]);
f[0][0][0] = g[0][0][0] = 0; For (i, 1, m) For (k, 0, K) {
if (!k) f[i][0][0] = g[i][0][0] = 0; For (j, 1, rw) {
f[i][j][k] = !k ? 0 : sum[rw][i] - sum[rw - j][i] +
max(f[i - 1][j][k], k & 1 ? g[i - 1][j - 1][k - 1]
: h[i - 1][j + 1][k - 1]);
}
For (j, 1, rw) g[i][j][k] = max(g[i][j - 1][k], f[i][j][k]);
Rof (j, rw, 1) h[i][j][k] = max(h[i][j + 1][k], f[i][j][k]);
}
int ans = -INF; For (i, 1, m) ans = max(ans, g[i][rw][K]); return ans;
}
int main() {
int i, j; n = read(); m = read(); K = read(); (K <<= 1)++;
For (i, 1, n) For (j, 1, m) a[i][j] = read();
For (i, 1, n) ans = max(ans, solve(i)); cout << ans << endl; return 0;
}