410. 分割数组的最大值

299 篇文章 1 订阅
227 篇文章 1 订阅

给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。

设计一个算法使得这 m 个子数组各自和的最大值最小。

示例 1:

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

输入:nums = [1,2,3,4,5], m = 2
输出:9
示例 3:

输入:nums = [1,4,4], m = 3
输出:4

提示:

1 <= nums.length <= 1000
0 <= nums[i] <= 106
1 <= m <= min(50, nums.length)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/split-array-largest-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

正式开始之前,先要感慨一下,写博客真的太有必要了,尤其是 二刷甚至 三刷算法的时候,能直接把你的思路拉回当当初写博客的那个思路,真的舒服,比如这个题,我第一次做是在 21 年的 11 月份,当时做完也是日常写博客写解析,今天再做的时候,发现根本读不懂,仿佛这个题是个新题,全 nm 忘了,所以我就去翻以前的博客,思路瞬间清晰,直接想起来了,三下五除二就搞定了,而且算法复盘真的很有必要,不多 bb 了,直接上解析:

本题,你可能刚开始读不懂题目,不过没关系,一点点分析,题目中说,要求 m 各子数组的最大值的最小值,比如 [7,2,5,10,8] 分为两部分,最大值的最小,那就是 725, 10 8,最大值是 18,如果是 72, 5 10 8,那么最大值就是 23 了,所以,可以推断出,答案在 数组的最大值 Math.max 和 数组总和之间,此时完全可以使用二分查找来解决,每次的 mid 值设置为 target,使用 checkOut 函数判断

checkOut 函数,就是本题的核心,其实就是一个分组的问题,以 target 为标准,当某个组,超过 target 了,就加一个新组,一看代码就懂了,最后统计的 count 就是分组的个数
如果分组的个数等于 m,但是不能保证是最优解(也即是最大值的最小值)继续缩短右边界,让 target 变小 求解。
如果分组的个数小于 m,说明分的组太少了,也就是 target 太大了,导致分组不够,此时缩短右边界,让 target 变小。
如果分组的个数大于 m,说明分的组太多了,也就是 target 的值小了,导致分的组多,此时缩短左边界,让 target 变大。

class Solution {
    public int splitArray(int[] nums, int m) {
        int left = 0, right = 0;
        for (int num : nums) {
            left = Math.max(left, num);
            right += num;
        }
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (checkOut(nums, mid, m)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
    public boolean checkOut(int[] nums, int target, int m) {
        int count = 1, sum = 0;
        for (int num : nums) {
            if (sum + num > target) {
                count++;
                sum = num;
            } else {
                sum += num;
            }
        }
        return count <= m;
    }
}
题目描述:给定一个非负整数数组nums一个整数m,你需要将这个数组分成m个非空连续数组。设计一个算法使得这m个子数组中的最大和最小。 解题思路: 这是一个典型的二分搜索题目,可以使用二分查找来解决。 1. 首先确定二分的左右边界。左边界为数组中最大的值,右边界为数组中所有元素之和。 2. 在二分搜索的过程中,计算出分割数组的组数count,需要使用当前的中间值来进行判断。若当前的中间值不够分割成m个子数组,则说明mid值偏小,将左边界更新为mid+1;否则,说明mid值偏大,将右边界更新为mid。 3. 当左边界小于等于右边界时,循环终止,此时的左边界即为所求的结果。 具体步骤: 1. 遍历数组,找到数组中的最大值,并计算数组的总和。 2. 利用二分查找搜索左右边界,从左边界到右边界中间的值为mid。 3. 判断当前的mid值是否满足题目要求,若满足则更新右边界为mid-1; 4. 否则,更新左边界为mid+1。 5. 当左边界大于右边界时,循环终止,返回左边界即为所求的结果。 代码实现: ```python class Solution: def splitArray(self, nums: List[int], m: int) -> int: left = max(nums) right = sum(nums) while left <= right: mid = (left + right) // 2 count = 1 total = 0 for num in nums: total += num if total > mid: total = num count += 1 if count > m: left = mid + 1 else: right = mid - 1 return left ``` 时间复杂度分析:二分搜索的时间复杂度为O(logN),其中N为数组的总和,而遍历数组的时间复杂度为O(N),因此总的时间复杂度为O(NlogN)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_努力努力再努力_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值