链接:https://ac.nowcoder.com/acm/contest/20960/1029
来源:牛客网
来源:牛客网
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
牛牛从牛毕那里拿了一根长度为n的白木板,木板被等分成了n段(没有被切割,只是虚拟划分成了n段),其中有些段被牛毕用颜料染成了黑色。
牛牛非常不喜欢黑色,它找来了一桶清洗剂决定对木板进行清洗,但是牛牛发现自己的清洗剂最多只能清洗m段。
清洗完后,牛牛会把木板锯成纯色的几段。例如假设木板是 (黑黑黑白白白白黑黑黑 ),就会被锯成(黑黑黑)(白白白白)(黑黑黑)三段。
牛牛想知道,它足够聪明地清洗木板,能获得的纯白色木板的最大长度是多少。
示例1
输入
6,1, [1,0,0,1,1,1]
返回值
4
说明
染成了[1,0,1,1,1,1]
示例2
输入
6,2, [1,0,0,1,1,1]
返回值
6
说明
染成了[1,1,1,1,1,1]本题的时间限制给了C++3秒,但如果枚举每一个可能的长度的话同样会超时。
所以需要进行改进,在枚举长度的时候为了求出某个区间里面是否符合题目中m的要求可以对0的个数求一个前缀和,从而方便的求出区间里面有多少个0来判断是否满足题目中的要求。
从而可以发现这个前缀和区间是单调递增的,那么我们就可以联想到1028-加减_2021秋季算法入门班第一章习题:模拟、枚举、贪心 (nowcoder.com)这道题里面的做法,同样可以定一个左下标,通过二分的方式来查找右下标。在中途通过前缀和快速验证寻找右下标。然后最后取最大值即为答案。
AC代码:
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param n int * @param m int * @param a intvector * @return int */ int solve(int n, int m, vector<int>& a) { // write code here int len = n; int cnt0[len]; int cnt = 0; //搞一个记录有多少个0的前缀和 for (int i=0;i<len;i++) { cnt = 0; if (a[i]==0) cnt=1; if (i==0) cnt0[i] = cnt; else cnt0[i] = cnt0[i-1]+cnt; } int sum, i, ans = -1; //i为左下标 for (i=0;i<len;i++) { int l = i, r = len-1; int num = -1; //二分去寻找右坐标 while (l<=r) { int mid = (l+r)>>1; if (i==0) { if (cnt0[mid]<=m) { num = max(num, mid-i+1); l = mid+1; } else r = mid-1; } else { if (cnt0[mid]-cnt0[i-1]<=m) { num = max(num, mid-i+1); l = mid+1; } else r = mid-1; } } ans = max(ans, num); } return ans; } };