[LeetCode] 410. Split Array Largest Sum

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:

Input: nums = [7,2,5,10,8], m = 2
Output: 18
Explanation:
There are four ways to split nums into two subarrays.
The best way is to split it into [7,2,5] and [10,8],
where the largest sum among the two subarrays is only 18.

Example 2:

Input: nums = [1,2,3,4,5], m = 2
Output: 9

Example 3:

Input: nums = [1,4,4], m = 3
Output: 4

Constraints:

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

分割数组的最大值。

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

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

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

这道题我给出一个比较好理解的二分法。这道题也有动态规划的做法,以后有机会再补充。

我第一次做这道题的时候觉得题意有点绕,这里重新解释一下。这道题是要你将数组分割成 M 个部分,分割之后每个子数组都是有一个各自的数组合的,所以一共会得到 M 个数组合。你要做的是找到一个分割方式使得这 M 个数组合中的最大值尽可能小。

这道题的二分法找的就是到底这个子数组的合是多少才比较合适。既然是查找就得有范围,这里查找的下界是数组里的最大值max,因为题目中给的 M 是有可能等于 input 数组长度的,也就是每个元素组成一个子数组;查找的上界是整个数组的前缀和,因为题目中给的 M 是有可能等于 1 的,也就是不分割这个数组。我这里用了一个helper函数去判断 mid 是否合适,其他的参见代码注释。

时间O(nlogn)

空间O(1)

Java实现

 1 class Solution {
 2     public int splitArray(int[] nums, int m) {
 3         int max = 0;
 4         long sum = 0;
 5         for (int num : nums) {
 6             max = Math.max(max, num);
 7             sum += num;
 8         }
 9 
10         // corner case
11         if (m == 1) {
12             return (int) sum;
13         }
14 
15         // normal case
16         long left = max;
17         long right = sum;
18         while (left <= right) {
19             long mid = left + (right - left) / 2;
20             if (helper(nums, m, mid)) {
21                 right = mid - 1;
22             } else {
23                 left = mid + 1;
24             }
25         }
26         return (int) left;
27     }
28 
29     private boolean helper(int[] nums, int m, long mid) {
30         // count记录目前分割了的子数组的个数
31         int count = 1;
32         long sum = 0;
33         for (int num : nums) {
34             sum += num;
35             // 如果子数组的和超过了设定的mid,那么就分割一块,count++
36             // 并且要把当前的这个num计算到下一个子数组里
37             if (sum > mid) {
38                 sum = num;
39                 count++;
40             }
41 
42             // 如果分割了超过M份,就退出
43             if (count > m) {
44                 return false;
45             }
46         }
47         return true;
48     }
49 }

LeetCode 题目总结

原文地址:https://www.cnblogs.com/cnoodle/p/14698854.html