LeetCode-Python-410. 分割数组的最大值

本文介绍了一种算法,用于将一个非负整数数组分成m个非空连续子数组,目标是使这些子数组各自和的最大值最小。通过分析题目,确定了结果范围,并使用二分查找法逐步缩小该范围,直至找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

注意:
数组长度 满足以下条件:

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

示例:

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

输出:
18

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

思路:

首先分析题意,可以得出结论,结果必定落在【max(nums), sum(nums)】这个区间内,因为左端点对应每个单独的元素构成一个子数组,右端点对应所有元素构成一个子数组。

然后可以利用二分查找法逐步缩小区间范围,当区间长度为1时,即找到了最终答案。

每次二分查找就是先算一个mid值,这个mid就是代表当前猜测的答案,然后模拟一下划分子数组的过程,可以得到用这个mid值会一共得到的子区间数cnt,然后比较cnt和m的关系,来更新区间范围。

本题跟1014. 在 D 天内送达包裹的能力非常类似。

class Solution(object):
    def splitArray(self, nums, m):
        """
        :type nums: List[int]
        :type m: int
        :rtype: int
        """
        # max(nums), sum(nums)
        if len(nums) == m:
            return max(nums)
        lo, hi = max(nums), sum(nums)
        while(lo < hi):
            mid = (lo + hi) // 2 # 最大和
            
            #------以下在模拟划分子数组的过程
            temp, cnt = 0, 1
            for num in nums:
                temp += num
                # cnt += 1
                if temp > mid:#说明当前这个子数组的和已经超过了允许的最大值mid,需要把当前元素放在下一个子数组里
                    temp = num
                    cnt += 1
            # print temp, cnt, mid
            #------以上在模拟划分子数组的过程
            
            if cnt > m: #说明分出了比要求多的子数组,多切了几刀,说明mid应该加大,这样能使子数组的个数减少
                lo = mid + 1
            elif cnt <= m:
                hi = mid

                
        return lo
            

以下是这种题型的可能的包装方法:

摘自:https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=499490&extra=&page=1

这题的剧本应该就是先问清题意,再简要说一下DP能怎么做,再来就直接开二分法,尝试把巧克力分成K分每一分都要超过一个猜测的Target,利口肆妖灵的反题

分享一下这题和反过来的利口肆衣领见过的几种包装方法:
1. 一片巧克力数组分成K块,数字代表甜度,第一个人会想拿最甜的一块,把他平分成一个状态使得第一个人拿到的总甜度是最小的,求第一人拿到的甜度
2. 一个必须依照数组顺序完成的工作,数字代表工作难易度,分成K天完工,尽可能把困难度最高的一天变得比较不累,求最累的一天的困难度
3. 一个数组代表一排的书,数字代表页数,现在必须把相邻的书分成K组放置到K台打印机,设置一个配置方法使得需要打印最多页数的机器打印最少页,求工作量最多的打印机需要打印的页数
4. 一个包裹数组,数字代表重量,依包裹排列顺序分成K批寄送,使得最重的一批重量最小,求最重一批重量(利口比赛刚出的)
题目描述:给定一个非负整数数组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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值