hdu6376 度度熊剪纸条

hdu6376 度度熊剪纸条

不愧是百度,出个题都这么恶臭,垃圾百度。

Description

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

纸条上依次写着 NN 个数字,数字只可能是 00 或者 11。

度度熊想在纸条上剪 KK 刀(每一刀只能剪在数字和数字之间),这样就形成了 K+1K+1 段。

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

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

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

input

有多组数据,读到EOF结束。

对于每一组数据,第一行读入两个数 NN 和 KK 。

第二行有一个长度为 NN 的字符串,依次表示初始时纸条上的 NN 个数。

0≤K<N≤100000≤K<N≤10000

所有数据 NN 的总和不超过100000

output

对于每一组数据,输出一个数,表示可能的最大前缀 11 的数量。

Sample

5 1
11010
5 2
11010
2
3

分析

先吐槽一下,垃圾百度,题意有歧义。

比如这个 1001100011

我们剪开:1 00 11 000 11

拼起来 1111100000 前缀1的数量是 5,这是题目的意思。

显然的:1.若第一个是1,那么我们剪下第一段1只需一次

​ 2.同样的,若最后一个是1,一次就能剪下最后一段。

​ 3.对于中间的1片段,我们要剪断两边,也就是剪两次

那这个问题就可以转化成01背包了,每段1的个数为价值,剪的次数为体积

你可能会有疑问,比如1110011110,只能剪一次

你可能会认为,前三个1已经是前缀了,为什么我们还要剪下来?后面的11110一次就能剪下,结果是7

不对!因为11110和前面没法拼到一起!

对于这种情况,我们可以把111的代价设为1,并且剪的次数加1。

这就相当于我们把前面的111单独拿出去,剩下0011110,对他求出最长前缀,111是肯定能接上去的。

这样dp的时候就自动考虑了这种情况,不用我们操心

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100005;
int n, k, w[maxn], dp[maxn], v[maxn];
char s[maxn];
int main(){
    while(scanf("%d%d", &n ,&k) != EOF){
        int tot = 0;
        memset(dp, 0, sizeof(dp));
        memset(w, 0, sizeof(w));
        memset(v, 0, sizeof(v));
        scanf("%s", s+1);
        for(int i=1; i<=n; i++){//求出每段1的“价值”和“体积”
            if(s[i] == '1' && s[i-1] == '1') w[tot] ++;
            else if(s[i] == '1' && s[i-1] != '1') w[++tot] = 1, v[tot] = 2;
        }
        k++;
        if(s[1] == '1') v[1] = 1;
        if(s[n] == '1') v[tot] = 1;//这两种情况特判
        if(k == 1){//剪0次的时候(前面k++)了
            if(s[1] == '1') printf("%d\n", w[1]);
            else printf("0\n");
            continue;
        }
        for(int i=1; i<=tot; i++){//01背包,不多说
            for(int j=k; j>=v[i]; j--){
                dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
            }
        }
        printf("%d\n", dp[k]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hzoi-poozhai/p/12704732.html