hdu6376 度度熊剪纸条-----01背包

题目:

   度度熊有一张纸条和一把剪刀。


    纸条上依次写着 N 个数字,数字只可能是 0 或者 1。
 
    度度熊想在纸条上剪 K 刀(每一刀只能剪在数字和数字之间),这样就形成了 K+1 段。

    他再把这 K+1 段按一定的顺序重新拼起来。

    不同的剪和接的方案,可能会得到不同的结果。

    度度熊好奇的是,前缀 1 的数量最多能是多少。   

Input
  有多组数据,读到EOF结束。
  对于每一组数据,第一行读入两个数 N 和 K 。
  第二行有一个长度为 N 的字符串,依次表示初始时纸条上的N个数。0≤K<N≤10000所有数据N的总和不超过100000
Output
  对于每一组数据,输出一个数,表示可能的最大前缀 1 的数量。

Sample Input
5 1
11010
5 2
11010
Sample Output
2
3


 

经过分析,不难看出,这道题实际上是在考 01背包。怎么是01背包呢?我们可以这么看:

可以把 1 的组数作为物品个数,每组里 1 的个数 作为每个物品的权值,然后把 剪 k 次 作为体积。这样的话貌似跑一遍零一背包就可以了。

这么简单吗......

然而枚举几个例子之后发现果然没那么简单....

就比如 1011101 ,现在让你只剪 1 刀,显然我们要剪成 10 和 111011,然后拼成 11101110,这样得到正确答案 3。而01背包的话,你根本不能剪出来 111。

根据这个例子,我们发现,这么多组1中,最后可以让其中一组1作为开头,那么那一组1的真实代价就会-1。那难道我们要一一枚举每一组1吗?

其实不然。我们把背包容量+1,就相当于其中一个物品的代价-1。这两种操作是等效的。

嗯,那就没问题了:背包体积+1,然后跑01背包。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 1e4 + 3;

int n, k, v[MAXN], w[MAXN], f[MAXN], cnt;
char a[MAXN];

int main(){
    while(scanf("%d%d",&n,&k) != EOF){
        memset(a, 0, sizeof(a));
        memset(f, 0, sizeof(f));
        memset(w, 0, sizeof(w));
        memset(v, 0, sizeof(v));
        cnt = 0;

        scanf("%s",a+1);
        for(int i=1;i<=n;i++){
            if(a[i]=='1'){
                cnt++;
                v[cnt] = 2;
                while(a[i]=='1' && i<n+1){
                    i++;
                    w[cnt]++;
                }
            }
        }
        if(a[1]=='1') v[1]=1;
        if(a[n]=='1') v[cnt]=1;

        if(++k == 1){
            if(a[1]=='1'){
                printf("%d\n",w[1]); continue;
            }else{
                printf("0\n"); continue;
            }
        }
        for(int i=1;i<=cnt;i++){
            for(int j=k;j>=v[i];j--)
                f[j] = max(f[j], f[j-v[i]]+w[i]);
        }
        printf("%d\n",max(f[k],0));
    }
    
    return 0;
}

 

posted @ 2020-04-16 19:01  Sky_Sparks  阅读(109)  评论(0编辑  收藏  举报