[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 是否合适,其他的参见代码注释。注意二分写法的不同,14,15 行和 20,21 行。

时间O(nlogn)

空间O(1)

Java实现

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

 

LeetCode 题目总结

posted @ 2021-04-25 01:29  CNoodle  阅读(114)  评论(0编辑  收藏  举报