链接:https://ac.nowcoder.com/acm/contest/20960/1029
来源:牛客网
时间限制:C/C++ 3秒,其他语言6秒
空间限制: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;
    }
};