Problem
Given an array nums
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.
Example 1:
1 | Input: nums = [7,2,5,10,8], m = 2 |
Example 2:
1 | Input: nums = [1,2,3,4,5], m = 2 |
Example 3:
1 | Input: nums = [1,4,4], m = 3 |
Constraints:
1 <= nums.length <= 1000
0 <= nums[i] <= 106
1 <= m <= min(50, nums.length)
Analysis
题目给出了一个数组nums
以及一个m
,要求把数组拆分成m
个子数组,计算这m
个子数组中最大的和,然后要找到一种拆分的方式使得这个和最小。对于数组拆分类的题目,因为所有元素都要使用到,所以一般会考虑dp。一般数组拆分类的dp定义都会是一个二维数组,比如说dp[i][j]
为把前j
个数字拆成i
组所能得到的最小的各个子数组和的最大值。遍历两个状态,i
的范围是[1, m]
,j
的范围是[1, n]
,在处理j
的时候,实际上需要遍历[1, j]
这个区间,以k
为分割点,前k
个数字的结果就是dp[i - 1][k]
(已知),后面数字的和作为一组。这里因为需要求后面数字的和,所以要首先计算前缀和。最后用min(dp[i - 1][k], presum[j] - presum[k])
去更新dp[i][j]
即可。答案就是dp[m][n]
。
这道题目除了dp,还有另一种方法可以做。我们重新看题,一是数组中所有的元素都要被使用到,二是有一个限制条件,三是要求的答案是一个子数组的和,是有规定区间的。这三个特点就指向了二分答案的思路。我们先来看答案的区间,两种极端case:
- 如果
m = 1
,说明整个数组就是答案,答案就是数组的元素和; - 如果
m = n
,说明每个数字单独是一个子数组,答案就是最大的那个数字。
上面说的就是答案的区间,得到区间之后我们就对答案进行二分搜索。接下来我们来看怎么二分,对于每一个mid
,我们找出小于等于这个mid
的区间数量x
,如果x < m
,说明mid
太大了,因此要向左半部分搜索;反之,要向右半部分搜索。特殊情况是x == m
,这里因为我们要的是最小的,所以应该是左边界,因此和x < m
的处理情况一样。
Solution
无
Code
Method1: Dynamic Programming
1 | class Solution { |
Method 2: Binary Search
1 | class Solution { |
Summary
这道题目属于难度比较大的数组类题目,dp的思路比较明确,但是遍历的范围难确定。二分的思路倒是很直观,但是比较难想到。这道题目的分享到这里,感谢你的支持!