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; }