BZOJ 3241 UOJ 125 NOI 2013 书法家

2 篇文章 0 订阅

13年国赛一道很有意思的一道题…… 然而和浙江那年省选题蚂蚁寻路思路一致,是巧合?

本题 dp , 从左往右逐列考虑, 将状态分为11种, 每个字符分成3类状态, 间隔有两种状态,我只想说说 N 字符的第二种状态的转移。

如果 x,y 能够转移到 l,r , 那么:

lxr+1 ryn

我们用坐标可视化这个不等式:

显然可用的 x,y 的区域是一个矩形。我们来看一些性质:
l 变化 r 不变时, 矩形仅有左边界变化。
r 变化 l 不变时, 矩形的上边界和右边界变化。

由此我们发现先枚举 r 更好, 然后我们尝试维护一个可选区域每一列的最大值。
具体方法是: r 从大到小枚举,纪录可选区域每一列的最大值, 则 r 变化时可选区域增加 y=r 这一行(如图中带黑色部分),我们暴力更新; l <script type="math/tex" id="MathJax-Element-1692">l</script> 从大到小枚举,并维护一个此时后缀最大值即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 155;
const int INF = 0x3f3f3f3f;

int n , m , g[maxn][510];
int d[3][11][maxn][maxn] , s[510][maxn] , mx[maxn];

int main(int argc, char *argv[]) {

    cin>>n>>m;
    for(int i=n;i>=1;i--) for(int j=1;j<=m;j++) scanf("%d" , g[i]+j);
    for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) s[i][j] = s[i][j-1] + g[j][i];

    int res = -INF;
    memset(d, -INF, sizeof(d));
    for(int i=1;i<=m;i++) 
    {
        int c = i%3 , f = (i-1)%3 , t = (i+1)%3;
        memset(d[t], -INF, sizeof(d[0]));

        // FOR 0 , 4 , 5 , 6 , 8 , 9 , 10
        for(int l=1;l<=n;l++) for(int r=l;r<=n;r++)
        {
            int s1 = s[i][r] - s[i][l-1] , s2 = g[l][i] + g[r][i];
            d[c][0][l][r] = max(d[f][0][l][r], 0) + s1;

            if(r-l < 2) continue;
            d[c][4][l][r] = d[f][3][0][0] + s1;
            d[c][6][l][r] = d[f][5][l][r] + s1; 
            d[c][5][l][r] = max(d[f][4][l][r], d[f][5][l][r]) + s2;
            d[c][8][l][r] = max(d[f][8][l][r], d[f][7][0][0]) + s2;
            d[c][9][l][r] = max(d[f][9][l][r], d[f][8][l][r]) + s1;
            d[c][10][l][r] = max(d[f][10][l][r], d[f][9][l][r]) + s2;

            res = max(res, d[c][10][l][r]);
            d[t][7][0][0] = max(d[t][7][0][0], d[c][6][l][r]);
        }

        // FOR 3 , 7
        d[c][3][0][0] = max(d[c][3][0][0], d[f][3][0][0]);
        d[c][7][0][0] = max(d[c][7][0][0], d[f][7][0][0]);

        // FOR 1
        memset(mx, -INF, sizeof(mx));
        for(int r=n;r;r--)
        {
            for(int j=1;j<=n;j++) mx[j] = max(mx[j], d[f][1][j][r]);
            for(int l=r,now=mx[r+1];l;l--)
            {
                now = max(now, mx[l]);
                d[c][1][l][r] = max(d[f][1][l][r], now) + s[i][r] - s[i][l-1];
            }

            for(int l=1,now=-INF;l<=r;l++)
            {
                d[c][1][l][r] = max(d[c][1][l][r], now + s[i][r] - s[i][l-1]);
                now = max(now, d[f][0][l][r]);
            }
        }

        // FOR 2
        for(int l=1;l<=n;l++) for(int r=l,now = -INF;r<=n;r++)
        {
            d[c][2][l][r] = max(d[f][2][l][r], now) + s[i][r] - s[i][l-1];
            d[t][3][0][0] = max(d[t][3][0][0], d[c][2][l][r]);
            now = max(now, d[f][1][l][r]);
        }
    }

    cout<<res<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值