• [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 题目总结

  • 相关阅读:
    键盘事件
    冒泡事件-捕获事件-阻止事件
    Date()常用属性
    dom树节点的增删改插
    boost/c++11 变量类型打印、相等判断以及成员函数指针相关
    c++ std:call_once 与 单例模式实现
    c++11 异步编程 std::async
    c++ STL中一些常用函数/模板
    c++11 std::atomic 原子操作
    C++ std::atomic_flag 实现spinLock
  • 原文地址:https://www.cnblogs.com/cnoodle/p/14698854.html
Copyright © 2020-2023  润新知