zoukankan      html  css  js  c++  java
  • bzoj1296(SCOI2009)粉刷匠

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1296

    这道题暴露出自己:

      1.对于区间与前缀的可转化性认识不足;

      2.对于分组背包不够熟练。

    很容易想到最后是一个分组背包,枚举总共刷 k 次,前几个木条刷了 k - j 次,当前木条刷 j 次,得到答案。

    所以我们需要每个木条“刷k次的最大正确数量”这一个值。

      自己只会做“前缀被全刷完的最少次数”(颜色相同就-1),于是想把它转化成这个问题,就是算出前缀被刷完的最小次数,用它更新“刷k次的最大数量”。

        但是又觉得不一定是前缀被刷完,可以是区间被刷完。于是尝试把那个写法搬到区间上;当然很混乱。

    1.其实“被刷完”这个状态不够灵活,不如就定义成“刷k次的最大数量”;只要想到(2)和(3),就知道这个定义还是可以转移的。

    2.如果是这个定义,用前缀的状态递推就足够,因为不一定每个都刷了颜色,就不用刻意分区间了。

    3.在求前缀的时候就是指定后 j 个只刷一次。这种划分的方法应该很熟悉它的正确性才行。

    4.分组背包可以在边算dp[i]的时候就边维护好。就算不这样而最后单独求一下,其实也挺好弄的。自己应该对它更熟练。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=55,T=2505;
    int n,m,t,a[N][N],dp[N][T],s[N],ans,f[N][T];
    int cal(int x,int y)
    {
        int a=s[y]-s[x-1];
        int b=y-x+1-a;
        return max(a,b);
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=n;i++)
        {
            memset(s,0,sizeof s);
    //        memset(dp,0,sizeof dp);
            for(int j=1;j<=m;j++)
            {
                scanf("%1d",&a[i][j]);s[j]=s[j-1]+(a[i][j]);
                for(int k=1;k<=j;k++)
                {
                    dp[j][k]=0;
                    for(int l=0;l<j;l++)
                        dp[j][k]=max(dp[j][k],dp[l][k-1]+cal(l+1,j));
                }
            }
            for(int k=1;k<=t;k++)    //k<=t
                for(int j=0;j<=k&&j<=m;j++)
                    f[i][k]=max(f[i][k],f[i-1][k-j]+dp[m][j]);
        }
        for(int i=1;i<=t;i++)ans=max(ans,f[n][i]);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    重载小于号
    无聊的会议
    程序内存和时间
    对拍
    读入和输出优化
    codevs 3269 混合背包
    清北第三套题
    codevs 2188 最长上升子序列
    清北第二套题
    [COGS896] 圈奶牛
  • 原文地址:https://www.cnblogs.com/Narh/p/9146392.html
Copyright © 2011-2022 走看看