3241: [Noi2013]书法家 DP

《根据数据范围猜做法系列》
首先感受一下题意,像一个DP。。。
然后观察一下数据范围,感觉应该是个三次方的DP。。
然后发现 n m小不少,应该是 O(n2m) 的。。。
然后观察一下图形,发现了非常奇妙的性质。。

可以分成这11部分,然后用 fi 表示第 i 块,枚举列进行转移。
大概有以下转移:
empty1,11,12,22,23,33,34,44,45,56,66,67,78,88,89,99,910,1010,1011,1111
然后大力DP就好了。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#define clear(a) memset(a,-63,sizeof(a))
using namespace std;

const int N=155;
const int M=505;
int n,m,f4,f8,ans,INF;
int a[M][N],s[N];
int f1[N][N],f2[N][N],f3[N][N],f5[N][N],f6[N][N],f7[N][N],f9[N][N],f10[N][N],f11[N][N],s1[N][N],s2[N][N];

inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}

int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            a[j][n-i+1]=read();
    clear(f1); clear(f2); clear(f3); clear(f5); clear(f6); clear(f7); clear(f9); clear(f10); clear(f11); clear(s1); clear(s2);
    INF=-f1[0][0]; f4=f8=ans=-INF;
    for (int j=1;j<=m;j++)
    {
        for (int i=1;i<=n;i++) s[i]=s[i-1]+a[j][i];
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                ans=max(ans,f11[l][r]=max(f11[l][r],f10[l][r])+a[j][l]+a[j][r]);
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                f10[l][r]=max(f10[l][r],f9[l][r])+s[r]-s[l-1];
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                f9[l][r]=max(f9[l][r],f8)+a[j][l]+a[j][r];
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                f8=max(f8,f7[l][r]);
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                f7[l][r]=f6[l][r]+s[r]-s[l-1];
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                f6[l][r]=max(f6[l][r],f5[l][r])+a[j][l]+a[j][r];
        for (int l=1;l<=n;l++)
            for (int r=l+2;r<=n;r++)
                f5[l][r]=f4+s[r]-s[l-1];
        for (int l=1;l<=n;l++)
            for (int r=l+1;r<=n;r++)
                f4=max(f4,f3[l][r]);
        for (int l=1;l<=n;l++)
        {
            int tmp=-INF;
            for (int r=l+1;r<=n;r++)
                tmp=max(tmp,f2[l][r-1]),f3[l][r]=max(f3[l][r],tmp)+s[r]-s[l-1];
        }
        for (int r=1;r<=n;r++)
        {
            int tmp=s2[r+1][r];
            for (int l=r;l;l--)
                tmp=max(tmp,s2[l][r]),f2[l][r]=max(s1[l-1][r],tmp)+s[r]-s[l-1];
        }
        for (int l=1;l<=n;l++)
            for (int r=l;r<=n;r++)
                f1[l][r]=max(0,f1[l][r])+s[r]-s[l-1];
        for (int l=1;l<=n;l++)
            for (int r=n;r;r--)
                s2[l][r]=max(f2[l][r],s2[l][r+1]);
        for (int r=1;r<=n;r++)
            for (int l=1;l<=n;l++)
                s1[l][r]=max(f1[l][r],s1[l-1][r]);
    }
    cout << ans << endl;
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值