这题强呀……打了10+30暴力之后苦想1h并不会做……于是去看题解。看题解的时候又莫名各种看错,结果看了好久才懂……记录一下血泪史吧。
这题不难发现走出来的图形就是一个高低高低的城堡型图案,命名为高峰跟低谷的话就是一共有k个低谷和k + 1个高峰,且交替出现。发现其实这个图形是由2 * k + 1个矩形所构成的,我们考虑将这样许多矩形看做转移的方向:f[i][j][p][h]代表以点(i, j)为左下角,当前枚举到的是第p个矩形,且高度为(i - h + 1)所能获得的最大权值。
转移方程:
(f[i][j][p][h] = left { f[i][j - 1][p][h], f[i][j - 1][p][h']] ight } + s[j][i] - s[j][h - 1]; )
其中第一部分代表和j - 1处于同一个矩形当中,第二部分则表示根据p的奇偶性选择<h或>h的转移。第二部分如果枚举,则复杂度太高了难以承受,所以用另一个数组g来保存,追加一维0/1来表示是>还是<中的最大值。然后发现其实 i 这一维是没有参与转移的,所以将这一维去掉,节约空间。期间有一个让我比较纠结的地方,就是
( ans = max(f[i][j][k][i], g[i][j][k][i][0]); )
起初并没有很理解为什么要将前一部分考虑进来。当高度等于i,不是说明这是一个低谷吗?为什么会符合条件呢?但其实有一种情况下是可以的:当没有低谷的时候,这样是一个符合条件的解。追加辅助数组的dp做的太少了,要多多加油呀ヾ(๑╹◡╹)ノ"
#include <bits/stdc++.h> using namespace std; #define INF 999999999 #define maxn 105 int n, m, k, ans = -INF, sum[maxn][maxn]; int f[maxn][maxn][maxn], g[maxn][maxn][maxn][2]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void work() { for(int p = 1; p <= k; p ++) for(int h = 1; h <= n; h ++) { f[0][p][h] = -INF; g[0][p][h][0] = g[0][p][h][1] = -INF; } for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { for(int p = 1; p <= k; p ++) { for(int h = 1; h <= i; h ++) { f[j][p][h] = max(f[j - 1][p][h], g[j - 1][p - 1][h][p % 2]); f[j][p][h] += sum[j][i] - sum[j][h - 1]; } g[j][p][1][0] = -INF; for(int h = 2; h <= i; h ++) g[j][p][h][0] = max(g[j][p][h - 1][0], f[j][p][h - 1]); g[j][p][i][1] = -INF; for(int h = i - 1; h >= 1; h --) g[j][p][h][1] = max(g[j][p][h + 1][1], f[j][p][h + 1]); } ans = max(ans, max(f[j][k][i], g[j][k][i][0])); } } int main() { n = read(), m = read(), k = read(); k = k * 2 + 1; for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { int t = read(); sum[j][i] += sum[j][i - 1] + t; } work(); printf("%d ", ans); return 0; }