给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
- 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批寄送,使得最重的一批重量最小,求最重一批重量(利口比赛刚出的)