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;
}
标签:11,剪纸,NN,hdu6376,int,maxn,度度,dp 来源: https://www.cnblogs.com/hzoi-poozhai/p/12704732.html