P7365 [CTSC2002]颁奖典礼 题解

蒟蒻的第一篇题解。

这是一道神奇的有关网格的 DP(一些大佬称其为暴力 DP),这里将 I 字母分为的三个部分称为第一,二,三部分。

做法:设 \(f_{i,j,k,l} \left( l \in [1,3] \right)\) 表示第 \(i\) 行,当前在第 \(l\) 部分,区间 \([j,k]\) 的最大面积。
由于第二部分必须比第一部分短一截,在暴力的时候就得在枚举第一部分的 \(j,k\) 时枚举第二部分的 \(j,k\),pia 的一下 \(m^4\) 了。
于是出现了一个小技巧:使用另一个数组 \(g_{i,j,k,l} \left( l \in [1,2] \right)\)

  • \(g_{i,j_g,k_g,1}\) 记录 \(f_{i,j_f,k_f,1} \left( j_f \in \left[ 1,j_g \right), k_f \in \left( k_g,m \right] \right)\) 的最大值;
  • \(g_{i,j_g,k_g,2}\) 记录 \(f_{i,j_f,k_f,2} \left( j_f,k_f \in \left( j_g,k_g \right) \right)\) 的最大值。

这样在不同部分的转移中就可以直接调用 \(g\) 数组而不用枚举 \(j_g,k_g\) 了。
最后我们会发现 \(i\) 这一维可以被轻松地压掉,但好像可以不压。
代码:

#include<bits/stdc++.h>
using namespace std;
inline int Max(int x, int y){return x > y ? x : y;}
inline int Min(int x, int y){return x < y ? x : y;}
inline int rd(){
    int x = 0;bool f = true;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-')f = false;c = getchar();}
    while(c <= '9' && c >= '0'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}
    return f ? x : -x;
}
const int N = 2e2 + 10;

int n, m, now, ans;
int pre[N];
int f[2][N][N][3], g[2][N][N][2];

int main(){
    memset(f[now^1], -0x3f, sizeof(f[now^1]));
    memset(g[now^1], -0x3f, sizeof(g[now^1]));
    n = rd(), m = rd();
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j)
            pre[j] = pre[j - 1] + rd();
        memset(f[now], -0x3f, sizeof(f[now]));
        memset(g[now], -0x3f, sizeof(g[now]));
        for(int j = 1; j <= m; ++j){
            for(int k = j; k <= m; ++k){
                if(pre[k] == pre[j - 1]){
                    f[now^1][j][k][0] = Max(0, f[now^1][j][k][0]);
                    f[now][j][k][0] = f[now^1][j][k][0] + k - j + 1;
                    f[now][j][k][1] = Max(g[now^1][j][k][0], f[now^1][j][k][1]) + k - j + 1;
                    f[now][j][k][2] = Max(g[now^1][j][k][1], f[now^1][j][k][2]) + k - j + 1;
                    ans = Max(ans, f[now][j][k][2]);
                }
            }
        }
        for(int j = 2; j <= m; ++j)
            for(int k = m - 1; k >= j; --k)
                g[now][j][k][0] = Max(f[now][j - 1][k + 1][0], Max(g[now][j - 1][k][0], g[now][j][k + 1][0]));
        
        for(int j = m - 2; j >= 1; --j)
            for(int k = j + 2; k <= m; ++k)
                g[now][j][k][1] = Max(f[now][j + 1][k - 1][1], Max(g[now][j + 1][k][1], g[now][j][k - 1][1]));
        
        now ^= 1;
    }
        
    printf("%d", ans);

    return 0;
}


posted @ 2022-11-04 20:53  Cotsheep  阅读(20)  评论(0编辑  收藏  举报