LeetCode 410. 分割数组的最大值 (二分套二分)

分割数组的最大值

  • 题意:给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

最小化最大值(最大化最小值)等问题模型,往往都可以用二分解决。

记数组中的最大值为 l l l,数组的元素总和为 r r r。那么显然答案必然在 [ l , r ] [l,r] [l,r]之间。
其次,如果能不超过 x x x的阈值去划分整个数组,那么 [ x + 1 , r ] [x+1,r] [x+1,r]中的所有数都是可行的。
反之,如果 y y y为阈值,那么 [ l , y − 1 ] [l,y-1] [l,y1]中的所有数都是不可行的。
而这就是二分的两段性

具体写判定函数的时候,还可以用二分优化。因为我们需要贪心的求出一段数并且这段数的和要小于等于阈值,而因为是非负数数组,所以这又可以用二分求出。

时间复杂度不超过: O ( l o g ( s u m − m m a x ) ∗ n ) O(log(sum-mmax)*n) O(log(summmax)n)

class Solution {
    
    
public: 
    vector<int> s;  //数据保证不会溢出
    int m, n ;
    int splitArray(vector<int>& a, int m) {
    
    
        n = a.size() , this->m = m;
        s.resize(n,0);
        s[0] = a[0];
        for(int i=1;i<n;i++){
    
    
            s[i] = s[i-1]+a[i];
        }
        int l = *max_element(a.begin(),a.end()), r = s[n-1];
        while(l<r){
    
    
            int mid = l+(r-l)/2;
            if(check(mid,a)){
    
    
                r = mid;
            }else{
    
    
                l = mid+1;
            }
        }
        return l;
    }

    bool check(int top,const vector<int>& a){
    
    
        int cnt = 0, x = 0;
        while(x<n){
    
    
            int l = x, r = n-1;
            while(l<r){
    
    
                int mid = (l+r+1)/2;
                int ss = s[mid];
                if(x>0) ss -= s[x-1];
                if(ss<=top){
    
    
                    l = mid;
                }else{
    
    
                    r = mid-1;
                }
            }
            cnt++;
            x = l+1;
        }
        return cnt<=m; 
    }
};

此题还可以用DP去解决。
DP做法

猜你喜欢

转载自blog.csdn.net/qq_44846324/article/details/108438026
今日推荐