【[SCOI2009]粉刷匠】

这好像是个暴力?

但是跑的挺快的

我们设\(dp[i][j][k]\)表示在第\(i\)行我们最远染到的位置是\(j\),这一行上一共染了\(k\)次最多能染对多少个格子

理性分析一下啊,每一行最多也就染\(m\)次,这样就能把这一行格子全部都染对

所以这个空间复杂度是\(nm^2\)

之后考虑一下转移

显然这就是一个非常经典的断点\(dp\)了,转移为

\[dp[i][j][k]=max(dp[i][p][k-1]+max(s_0[p+1,j],s_1[p+1,j])) \]

\(s_0[p+1,j]\)表示这个区间内有几个\(0\),后面那个也就是我们能够正确覆盖的格子数量

最后用分组背包合并大案就好了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define re register
int dp[51][51][51]; 
int pre[51][51][2];
char S[51][51];
int f[2501];
int n,m,T;
int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for(re int i=1;i<=n;i++)
    {
        scanf("%s",S[i]+1);
        for(re int j=1;j<=m;j++)
        if(S[i][j]=='1') pre[i][j][1]=pre[i][j-1][1]+1,pre[i][j][0]=pre[i][j-1][0];
            else pre[i][j][1]=pre[i][j-1][1],pre[i][j][0]=pre[i][j-1][0]+1;
    }
    for(re int i=1;i<=n;i++)
        for(re int j=1;j<=m;j++)
            for(re int k=1;k<=j;k++)
                for(re int p=0;p<j;p++)
                    dp[i][j][k]=max(dp[i][j][k],dp[i][p][k-1]+max(pre[i][j][0]-pre[i][p][0],pre[i][j][1]-pre[i][p][1]));
    for(re int i=1;i<=n;i++)
        for(re int j=T;j>=0;j--)
            for(re int k=0;k<=m;k++)
                if(j-k>=0) f[j]=max(f[j],f[j-k]+dp[i][m][k]);
    printf("%d\n",f[T]);
    return 0;
}

posted @ 2019-01-02 12:14  asuldb  阅读(119)  评论(0编辑  收藏  举报