[BZOJ3111][Zjoi2013]蚂蚁寻路(DP)

orz DP 。

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=3111

Solution

画个图可以发现是长城一样的形状。具体是:
2 K + 1 个非空矩形顺次横向相接,这些矩形底部所处的行号相等。
对于第 i ( i > 1 )
如果 i 是奇数,那么第 i 个矩形严格高于第 i 1 个矩形。
否则 i 是偶数,那么第 i 个矩形严格低于第 i 1 个矩形。
枚举封闭图形最后一行所处的行号(记为 r w ):
定义状态: f [ i ] [ j ] [ k ] 表示由 k 个矩形组成,第 k 个矩形的右下角为 ( r w , i ) ,高度为 j ,前 k 个矩形的最大权值和。
注: s u m ( l , r , i ) 表示第 i 列从第 l 个格子到第 r 个格子的权值之和,可以用前缀和 O ( 1 ) 求出。
转移当然是从 ( r w , i 1 ) 转移到 ( r w , i )

f [ i ] [ j ] [ k ] = f [ i 1 ] [ j ] [ k ] + s u m ( r w j + 1 , r w , i )

可以发现,上面的转移忽略了一种情况:第 k 个矩形只占用一列。
所以,当 i 为奇数时:
f [ i ] [ j ] [ k ] = max ( f [ i ] [ j ] [ k ] , f [ i 1 ] [ < j ] [ k 1 ] + s u m ( r w j + 1 , r w , i ) )

否则 i 为偶数时:
f [ i ] [ j ] [ k ] = max ( f [ i ] [ j ] [ k ] , f [ i 1 ] [ > j ] [ k 1 ] + s u m ( r w j + 1 , r w , i ) )

上面两个转移在实现上可以用 前缀 / 后缀最大值 将转移优化到 O ( 1 )
边界比较复杂:
0 i m , 1 j r w , f [ i ] [ j ] [ 0 ] = 0

0 i m , f [ i ] [ 0 ] [ 0 ] = 0

其余初始值为
第一个边界比较好理解,关键是第二个边界中为什么第二维可以为 0
答案是:当第一个矩形的高度为 1 时,该状态不可能从 f [ . . . ] [ 1 ] [ 0 ] 转移。 (因为高度要 严格大于前一个矩形)
行号上界为 r w 时的最优解为:
i = 1 m j = 1 r w f [ i ] [ j ] [ 2 K + 1 ]

复杂度 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;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80885760