[SCOI2009]粉刷匠(动态规划,序列dp,背包)

Posted terribleterrible

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2009]粉刷匠(动态规划,序列dp,背包)相关的知识,希望对你有一定的参考价值。

技术分享图片

分别对每块木板做区间dp,设(g[i][j])表示前i个格子,刷恰好j次,并且第i格是合法的最多合法的格子数.从前往后枚举断点来转移就好了.

这样处理再出来(g[i][j])每一块木板i刷j次的最大合法格子数.

最后再合并每块木板的答案,用(dp[i][j])表示前i块木板,一共恰好刷了k次的最大合法格子数,用刷表法暴力背包合并就好了.

很详细的注释.

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 55
using namespace std;
int n,m,T,ans,sum[maxn],dp[maxn][maxn*maxn];
int f[maxn][maxn],g[maxn][maxn];
char s[maxn];
//分别对每块木板区间dp
//再用背包来合并
int main()
{
    cin>>n>>m>>T;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        memset(g,0x8f,sizeof(g));
        g[0][0]=0;
        for(int j=1;j<=m;j++)sum[j]=sum[j-1]+(s[j]=='1');//蓝色的前缀和
        //g[j][k]当前木板表示前j个格子,刷k次的最多合法的格子数,并且j是合法的格子
        for(int j=1;j<=m;j++)
            for(int k=1;k<=j;k++)
                for(int l=0;l<j;l++)
                {
                    if(s[j]=='1')g[j][k]=max(g[j][k],g[l][k-1]+sum[j]-sum[l]);
                    else g[j][k]=max(g[j][k],g[l][k-1]+j-l-sum[j]+sum[l]);
                }
        //f[j][k]表示第i块木板,恰好刷k次的最多合法格子数
        for(int j=0;j<=m;j++)
            for(int k=0;k<=j;k++)
                f[i][k]=max(f[i][k],g[j][k]);
    }
    //dp[i][j]表示前i个块木板,恰好刷j次的最多的合法格子数
    for(int i=1;i<=n;i++)
        for(int j=0;j<=T&&j<=i*m-m;j++)
            for(int k=0;k<=m;k++)
                dp[i][j+k]=max(dp[i][j+k],dp[i-1][j]+f[i][k]);
    for(int i=0;i<=T;i++)ans=max(ans,dp[n][i]);
    cout<<ans<<endl;
    return 0;
}

以上是关于[SCOI2009]粉刷匠(动态规划,序列dp,背包)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1296: [SCOI2009]粉刷匠(DP)

[Bzoj 1296][Scoi2009] 粉刷匠 [DP + 分组背包]

P4158 [SCOI2009]粉刷匠(dp)

LG4158 「SCOI2009」粉刷匠 线性DP

题解 P4158 [SCOI2009]粉刷匠

[SCOI2009]粉刷匠の思路