链接:https://ac.nowcoder.com/acm/contest/893/F
来源:牛客网
Black & White
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
你有一个长度为 n 的 01 串S,你可以执行最多 m 次操作。
对于每次操作,你可以选择一个位置 i 满足 1≤i≤n1≤i≤n,翻转这一位的值,0变成1,1变成0。
定义一个 01 串的价值为其中最长连续0的个数和最长连续1的个数的较大值,求S在经过最多m次操作后的最大价值。
输入描述:
* 第一行一个整数 T ,表示接下来有 T 个样例。 * 首先输入n,m,表示S串的长度n和操作次数m,其中1≤n≤1000001≤n≤100000,0≤m≤10000≤m≤1000; * 接下来输入一个长度为n的字符串S。
输出描述:
一个整数,表示题面上描述的最大价值。
示例1
输入
复制
2 5 1 00101 2 1 01
输出
复制
4 2
说明
第一个串翻转第三个位置,00001的价值为4;第二个串翻转第一个位置,11的价值为2。
思路:用pre0[i]表示前i个数字中有多少个0,pre1[i]表示前i个数字中有多少个1,然后对答案进行二分,假设当前答案为x,那么应该有一个长度为x的区间,它的值是一样的,那么遍历整个区间,查询pre0[i+x]-pre0[i]是否小于等于m,如果是,说明有,那么扩大答案继续查询(二分操作),否则,缩小答案继续查询。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int maxn=1e5+10;
char s[maxn];
char temp[maxn];
int pre0[maxn];
int pre1[maxn];
int n,m;
bool solve(int x){
for(int i=0;i+x<=n;i++){
if(pre0[i+x]-pre0[i]<=m)return 1;
if(pre1[i+x]-pre1[i]<=m)return 1;
}
return 0;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
scanf("%s",s);
memset(pre0,0,sizeof(pre0));
memset(pre1,0,sizeof(pre1));
for(int i=1;i<=n;i++){
temp[i]=s[i-1];
}
for(int i=1;i<=n;i++){
pre0[i]=temp[i]=='0'?pre0[i-1]+1:pre0[i-1];
pre1[i]=temp[i]=='1'?pre1[i-1]+1:pre1[i-1];
}
int low=1,high=n;
while(low<=high){
int mid=(low+high)>>1;
if(solve(mid))low=mid+1;
else{
high=mid-1;
}
}
printf("%d\n",high);
}
return 0;
}