LeetCode410.分割数组的最大值

51 阅读3分钟

题目链接🔗: leetcode.cn/problems/sp…

题目描述:

image.png

思路+代码:

#include<iostream>
#include<vector>
#include<algorithm>

//	最大值最小化问题,同样二分答案,要最小化的最大值有上下界,
//	上界是 sum,下界是 nums 中的最大值,二分尝试这个范围里的每个
//	可能的答案,每一轮通过贪心计算要以当前的估计值作为划分后子数组
//	最大值的话,最少要划分成多少组才能满足,然后用这个计算结果和
//	真实的k作比较。首先要明白这里为什么贪心(这里也就是顺序遍历处理)能
//	实现局部最优到全局最优,首先子数组是连续的,其次第一组贪心策略放入的元素
//	数目一定不会少于其他策略放入的元素数目,后面的分组当然也是同样的道理,
//	换句话说,要在指定最大值限制下分成最少的组数,那我每组的元素当然有尽可能多放,不然留到
//	后面,肯定要占另外的组,也就有可能会造成要划分出新的组,增加了组数。
//	然后再看怎么二分,如果算出来的最小需要划分成的组数小于 k,那也就是
//	说要划分成更多的组,此时我们可以往更小估值,这样可能会有和值比较大的子数组
//	拆开,以使得组数增加; 如果算出来的最小需要划分成的组数等于 k,那就继续往小估,
//	因为有可能有更小的最大值也能满足最小需要组数等于 k, 注意这里只是可能,所以
//	r 不能移到 mid - 1, 而是移到 mid; 如果算出来的最小需要划分成的组数大于 k,
//	便要通过增大估值,这样可能会有和值比较小的子数组被合并,以使得组数减少。
//	
//	这里也做了小的优化,就是当当前估的最大值不是任何贪心划分出来的子数组和的时候, 
//	我们在贪心的时候获取实际子数组中和的最大值,然后在 min_k<=k 同时 max_sum < m 的
//	时候直接 r = max_sum。 
//	
//	复杂度:O(nlog(sum)) 


std::vector<int> get_min_k(std::vector<int>&nums, int m){
        int t = nums.size();
        int count = 0, tem_sum = 0, max_sum = -1;
        for(int i=0; i<t; i++){
                if(tem_sum+nums[i]>m){
                        count ++;
                        max_sum = std::max(max_sum, tem_sum);
                        tem_sum = nums[i];
                }
                else{
                        tem_sum += nums[i];
                }
        }
        return {count+1, std::max(max_sum, tem_sum)};
}

int splitArray(std::vector<int>& nums, int k) {
    int t = nums.size(), res = 0;
    int sum = 0, max_num = -1;
    for(int i = 0; i<t; i++){
        sum += nums[i];
        max_num = std::max(max_num, nums[i]);
        }
        int l = max_num, r = sum;
        while(l<r){
                int m = (l+r)/2;
                int min_k = get_min_k(nums, m)[0];
                int max_sum = get_min_k(nums, m)[1];
                if(min_k<=k) max_sum < m ? r = max_sum : r = m;
                else l = m + 1;
        }
        return l;
}

int main(){
        int n, a, k;
        std::cin >> n;
        std::vector<int> nums; 
        for(int i=0; i<n; i++){
                std::cin >> a;
                nums.push_back(a); 
        }
        std:: cin >> k;
        std:: cout << splitArray(nums, k);
        return 0;
}