http://acm.hdu.edu.cn/showproblem.php?pid=6376
POINT:
把连续的1都预处理出来。
这样会有三种连续的1, 开头的,中间的,结尾的。
首先确定哪个放在【前缀1的最后一部分】。
- 如果是开头,开头就不用切,中间的要切2次才能放在前面,结尾切一次。类似: (中间1)(中间2)(中间3)(结尾)(开头)
- 如果是中间1,那么这个中间1只要切1次,因为后面的0不影响。但是开头【如果要】就要切1次了,类似:(中间2)(中间3)(结尾)(开头)(中间1)
- 如果是结尾,那么这个结尾要切一次,开头【如果要】要切一次,中间的都要2次
我用背包做的,应该可以贪心。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL N = 10000+33;
const LL mod = 998244353;
char s[N];
struct node
{
int num;
int k;
}a[N];
int dp[N];
int cnt;
int f(int k)
{
memset(dp,0,sizeof dp);
for(int i=1;i<=cnt;i++)
{
for(int j=k;j>=a[i].k;j--){
dp[j]=max(dp[j],dp[j-a[i].k]+a[i].num);
}
}
return dp[k];
}
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k)){
scanf("%s",s+1);
int num=0;
int i;
cnt=0;
for(i=1;i<=n;i++){
if(s[i]=='1'){
num++;
}else{
break;
}
}
a[++cnt].num=num;
a[cnt].k=0;
num=0;
int ans=a[1].num;
for(;i<=n;i++){
if(s[i]=='1')
num++;
else{
if(num==0) continue;
a[++cnt].num=num;
a[cnt].k=2;
num=0;
}
}
a[++cnt].num=num;
a[cnt].k=1;
ans=max(ans,f(k));
int Max=0,kk=0;
for(int i=2;i<cnt;i++){
if(Max<a[i].num){
Max=a[kk=i].num;
}
}
if(kk!=0&&k>=1){
a[1].k=1;
a[kk].k=0;
ans=max(ans,f(k-1));
a[kk].k=2;
}
if(k>=1&&a[cnt].num!=0){
a[cnt].k=0;
ans=max(ans,f(k-1));
}
printf("%d\n",ans);
}
return 0;
}