【281、leetcode410 分割数组的最大值。】

143 阅读2分钟

题目描述:

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

示例 1:

输入: nums = [7,2,5,10,8] m = 2 输出: 18 解释: 一共有四种方法将nums分割为2个子数组。 其中最好的方式是将其分为[7,2,5]和[10,8], 因为此时这两个子数组各自的和的最大值为18,是所有情况中最小的。

Java代码实现:

class Solution {
    public int splitArray(int[] nums, int m) {
        int left = 0;
        int right = 0;
        for (int i = 0; i < nums.length; i++) {
            right += nums[i];
            if (left < nums[i]) {
                left = nums[i];
            }
        }
        while (left < right) {
            int mid = left + (right - left) / 2;
            int sum = 0;
            int count = 1;
            for (int i = 0; i < nums.length; i++) {
                if (sum + nums[i] > mid) {
                    count++;
                    sum = nums[i];
                } else {
                    sum += nums[i];
                }
            }
            if (count <= m) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}

解题思路:

这道题需要将数组分成m个子数组,且各自和的最大值最小,因此可以使用二分查找来解决。

首先,通过遍历整个数组,可以得到数组的左右边界。左边界是数组中的最大值,右边界是数组所有元素的和。这里使用了一个贪心的策略,将数组分成一个元素一个元素的子数组时,可以得到最大的最小和值。

然后,在[left, right]区间进行二分查找,计算mid值,以mid值作为各子数组和的最大值,计算当前分割数组的子数组数量。如果子数组数量小于等于m,则说明当前mid值合法,将右边界移到mid值,否则说明mid值过小,需要将左边界移到mid+1。当左边界等于右边界时,退出循环,返回此时的左边界即可。

这里需要注意的是,二分查找的循环条件是left < right,而不是left <= right。这是因为当left=right时,说明此时的mid值已经被计算过了,如果再进行一次计算,会导致重复计算,因此需要将循环条件限制在left<right。