[Leetcode] 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:
If n is the length of array, assume the following constraints are satisfied:

  • 1 ≤ n ≤ 1000
  • 1 ≤ m ≤ min(50, n)

Examples:

Input:
nums = [7,2,5,10,8]
m = 2

Output:
18

Explanation:
There are four ways to split nums into two subarrays.
The best way is to split it into [7,2,5] and [10,8],
where the largest sum among the two subarrays is only 18.

思路

我一开始直觉这道题目可以用动态规划来求解,可是发现无论时间复杂度还是空间复杂度都高达O(n^3),不是一个好的方法。后来在网上发现用二分查找法求解本题十分巧妙,解释如下:

我们直接要求这个最小值是比较困难的,但是给定一个值x,判断是否存在一种划分,使得每个子区间的和都不超过x,则是容易的。我们可以贪心地遍历一遍数组,不断求和,直到和超过了x值,再新分出一个子区间,最后检查划分出来的子区间数是否超过了m。这个检查的时间复杂度仅为O(n)。

然后就可以不断地判断x是否满足上述条件,如果满足说明我们要求的解不超过x,否则说明要求的解大于x,这就构成了一个二分查找的条件。那么x的取值范围可能是什么呢?其最小值显然是数组中元素的最大值,而最大值显然是数组中所有元素之和。接下来,一切都顺理成章了^_^。

假设数组中的元素个数是n,元素之和是m,则算法的时间复杂度是O(nlogm),其空间复杂度仅为O(1)。

代码

class Solution {
public:
    int splitArray(vector<int>& nums, int m) {
        long long left = 0, right = 0;
        for (int i = 0; i < nums.size(); ++i) {
            left = max(left, (long long)nums[i]);       // the possible minimal value
            right += nums[i];                           // the possible maximal value
        }
        while (left <= right) {
            long long mid = left + (right - left) / 2;
            if (valid(nums, m - 1, mid)) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return left;
    }
private:
    bool valid(const vector<int> &nums, int cuts, long long max_value) {
        int acc = 0;
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] > max_value) {
                return false;
            }
            else if (acc + nums[i] <= max_value) {
                acc += nums[i];
            }
            else {
                --cuts;
                acc = nums[i];
                if (cuts < 0) {
                    return false;
                }
            }
        }
        return true;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值