FZU 2041 二分枚举
思路:先O(n)预处理出ri[i][j],le[i][j],分别表示第i个位置向右边移动出j个空格需要的步数,表示第i个位置向左边移动出j个空格需要的步数。
然后枚举间隙处,二分判段最大间隔。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define Maxn 710 #define inf 100000000 using namespace std; int ri[Maxn][Maxn],le[Maxn][Maxn],n,m; char str[Maxn]; bool OK(int pos,int x) { int i,j,cnt=inf; if(pos==1) { cnt=min(cnt,ri[pos][x]); return cnt<=m; } for(i=0;i<=n;i++){ if(i>x) break; cnt=min(cnt,ri[pos][i]+le[pos-1][x-i]); } //cout<<pos<<" "<<x<<" "<<cnt<<" "<<m<<endl; return cnt<=m; } int main() { int t,i,j,ze,Ca=0; scanf("%d",&t); while(t--){ for(i=1;i<Maxn;i++){ for(j=1;j<Maxn;j++){ ri[i][j]=le[i][j]=inf; } } ze=0; scanf("%d%d%s",&n,&m,str); if(str[0]=='0') le[1][1]=0,ze=1; for(i=1;i<n;i++){ if(str[i]=='0') ze++; for(j=1;j<=n;j++){ if(le[i][j-1]>=inf) break; if(str[i]=='1') le[i+1][j]=le[i][j]+j; else le[i+1][j]=le[i][j-1]; } } if(str[n-1]=='0') ri[n][1]=ri[n+1][1]=0; for(i=n-1;i>=1;i--){ for(j=1;j<=n;j++){ if(ri[i+1][j-1]>=inf) break; if(str[i-1]=='1') ri[i][j]=ri[i+1][j]+j; else ri[i][j]=ri[i+1][j-1]; } } int f=0; int ans=0,l,r,mid; for(i=1;i<=n;i++){ if(str[i-1]=='0'){ l=0;r=ze; while(l+1<r){ mid=(l+r)>>1; if(OK(i,mid)) l=mid; else r=mid; } ans=max(ans,l); if(OK(i,r)) ans=max(ans,r); } } printf("Case %d: %d\n",++Ca,ans); } return 0; }