题目链接:https://vjudge.net/problem/FZU-2041
题意:n的01串,可以移动1 m次,求最长的连续0的长度
题解:每一段的0受左右最近的1影响,我们就枚举相邻的两个1,来求m次操作,使这两个1之间的0的连续长度最长, 预处理dp[i][j]表示i位置的1,移动到j位置的花费,然后我们枚举两个1的时候,前面的1枚举向左的位置,后面的1二分来求
#include<iostream>
#include<cstdio>
using namespace std;
#define INF 0x3f3f3f3f
int dp[510][510];
int l[510],r[510];
int n,m;
char s[510];
int main()
{
int T;
int prepos;
int pprepos;
int presum;
int sum;
int pos;
int nn=1;
int ans;
int cnt;
int ll,rr,mid;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=INF;
prepos=0;
presum=0;
sum=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='1')
{
dp[i][i]=0;
for(int j=i-1;j>prepos;j--)
dp[i][j]=i-j;
for(int j=prepos;j>presum;j--)
dp[i][j]=i-j+dp[prepos][j-1];
prepos=i;
presum++;
pos=i;
sum++;
l[i]=sum;
}
}
for(int i=0;i<=n+2;i++)
dp[n+1][i]=0;
prepos=n+1;
presum=0;
sum=0;
for(int i=n;i>=1;i--)
{
if(s[i]=='1')
{
dp[i][i]=0;
for(int j=i+1;j<prepos;j++)
dp[i][j]=j-i;
for(int j=prepos;j<=n-presum;j++)
dp[i][j]=j-i+dp[prepos][j+1];
prepos=i;
presum++;
sum++;
pos=i;
r[i]=sum;
}
}
// for(int i=1;i<=n;i++)
// for(int j=1;j<=n;j++)
// printf("%d%c",dp[i][j]," \n"[j==n]);
printf("Case %d: ",nn++);
if(sum==0) printf("%d\n",n);
else if(sum==1)
{
printf("%d\n",min(n-1,max(n-(pos-m),pos+m-1)));
}
else
{
ans=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='1')
{
prepos=i;
break;
}
}
for(int i=prepos+1;i<=n;i++)
{
if(s[i]=='1')
{
for(int j=prepos;j>=l[prepos];j--)
{
if(dp[prepos][j]>m) break;
ll=i,rr=n-r[i]+1;
while(ll<=rr)
{
mid=(ll+rr)>>1;
if(dp[i][mid]+dp[prepos][j]<=m)
{
ans=max(ans,mid-j-1);
ll=mid+1;
}
else
rr=mid-1;
}
}
prepos=i;
}
}
printf("%d\n",ans);
}
}
return 0;
}