[LeetCode 410] Split Array Largest Sum (二分答案/最大值最小化)

410. Split Array Largest Sum

Given an array which consists of non-negative integers and an integer m, you can split the array into m non-empty continuous subarrays. Write an algorithm to minimize the largest sum among these m subarrays.

Note:
Given m satisfies the following constraint: 1 ≤ m ≤ length(nums) ≤ 14,000.


题解

  本题是说给定一个非负整数数组A和一个正整数m,将A分为m个子区间,显然有很多种分法,每一种划分,都可以找到一个子区间和的最大值sum,现在要求的是在各种划分中,sum的最小值。
  本题可以使用二分答案的方法来做。要求这个最小值是比较难的,但是给定一个值x,判断是否存在一种划分使得每个子区间的和都不超过x,这是容易的。我们可以贪心地遍历一遍数组,不断求和,直到和超过了x值,再新分出一个子区间,最后检查划分出的子区间数是否超过了m。这个检查的时间复杂度为O(n).
  然后就可以不断的询问x是否满足上述条件,如果满足说明我们要求的解不超过x,否则说明要求的解大于x,这就构成了一个二分的条件。我们先猜测x属于一个足够大的范围,然后检查中间值是否满足条件,不管结果如何,我们都可以将猜测区间减半。如此不断的缩减区间,就得到了最后的解。因为int的值不超过2^31,所以需要O(log(2^31))=O(1)次检测,因此算法复杂度是O(n)的。


代码:

class Solution {
public:
    int splitArray(vector<int>& nums, int m) {
        unsigned lo = 1, hi = 0x7fffffff;
        while (lo < hi) {
            unsigned mi = (lo+hi)/2;
            if (check(nums, m, mi)) hi = mi;
            else lo = mi+1;
        }
        return lo;
    }

    bool check(vector<int>& nums, int m, unsigned x) {
        unsigned sum = 0;
        int cnt = 1;
        for (int i = 0; i < nums.size(); i++) {
            if (sum + nums[i] <= x) {
                sum += nums[i];
            }
            else {
                cnt += 1;
                if (nums[i] > x) return false;
                sum = nums[i];
                if (cnt > m) return false;
            }
        }
        return cnt <= m;
    }
};

你可能感兴趣的:(OJ题解)