BZOJ 3241 UOJ 125 NOI 2013 书法家

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

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

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

lxr+1 ryn

我们用坐标可视化这个不等式:
BZOJ 3241 UOJ 125 NOI 2013 书法家_第1张图片

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

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

#include 
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<return 0;
}

你可能感兴趣的:(dp,NOI)