JavaInterview JavaInterview
首页
指南
分类
标签
归档
  • CSDN (opens new window)
  • 文档集合 (opens new window)
  • 系统架构 (opens new window)
  • 微信号 (opens new window)
  • 公众号 (opens new window)

『Java面试+Java学习』
首页
指南
分类
标签
归档
  • CSDN (opens new window)
  • 文档集合 (opens new window)
  • 系统架构 (opens new window)
  • 微信号 (opens new window)
  • 公众号 (opens new window)
  • 指南
  • 简历

  • Java

  • 面试

  • 算法

  • algorithm
  • leetcode
JavaInterview.cn
2022-07-09
目录

分割数组的最大值Java

文章发布较早,内容可能过时,阅读注意甄别。

# 题目

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

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

示例 1:

输入:nums = [7,2,5,10,8], m = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。 
其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

示例 2:

输入:nums = [1,2,3,4,5], m = 2
输出:9

示例 3:

输入:nums = [1,4,4], m = 3
输出:4

提示:

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

# 思路

    /*
    二分法
    nums = [7,2,5,10,8]
    m = 1,那么整个数组作为一部分,最小的最大值为 32
    m = n,那么每个元素作为一个子数组,从所有元素选取最大值,最小的最大值小为 10

    m 的取值范围为 1 <= m <= n,因此,最大值的最小值的范围为 [10, 32]

    我们利用二分法查找,找出符合 m 的最大值的最小的结果
    二分过程:
    left = 10;
    right = 32
    mid = (left + right) >>> 1 = 21(这个 21 就是一个子数组的最大容量)
    我们假设刚开辟的用来存储的子数组个数 cnt = 1
    那么根据贪心思想,我们将数组元素按顺序逐个往里放
    因此就有如下过程:
    7 < 21
    7 + 2 < 21
    7 + 2 + 5 < 21
    7 + 2 + 5 + 10 > 21
     至此,我们可以看出一个 21 容量的子数组是无法容纳整个数组元素的,因此我们需要开辟第二个子数组来存储剩下的数组元素
    cnt = cnt + 1 = 2
    10 < 21
    10 + 8 < 21
    我们发现,两个子数组可以将整个数组元素放入,而 cnt 刚好等于 m,因此 [7,2,5] 和 [10,8] 就是分割出来的两个子数组,最小的最大值为 18

    为什么是放入元素直到放不下为止?因为要求的是连续子数组,我们需要保证每个连续的子数组的元素和都尽可能的接近 21

    如果我们最终得到的 cnt > m,那么表示我们划分出太多的子数组,也就是意味着一个子数组的容量太少,我们需要再扩大容量,即 left = mid + 1,然后继续进行二分
    如果我们最终得到的 cnt < m,那么表示我们划分出太少的子数组,也就是意味着一个子数组的容量太大,需要减少容量,即 right = mid - 1
    */

# 解法



class Solution {
        /*
        二分法
        nums = [7,2,5,10,8]
        m = 1,那么整个数组作为一部分,最小的最大值为 32
        m = n,那么每个元素作为一个子数组,从所有元素选取最大值,最小的最大值小为 10

        m 的取值范围为 1 <= m <= n,因此,最大值的最小值的范围为 [10, 32]

        我们利用二分法查找,找出符合 m 的最大值的最小的结果
        二分过程:
        left = 10;
        right = 32
        mid = (left + right) >>> 1 = 21(这个 21 就是一个子数组的最大容量)
        我们假设刚开辟的用来存储的子数组个数 cnt = 1
        那么根据贪心思想,我们将数组元素按顺序逐个往里放
        因此就有如下过程:
        7 < 21
        7 + 2 < 21
        7 + 2 + 5 < 21
        7 + 2 + 5 + 10 > 21
         至此,我们可以看出一个 21 容量的子数组是无法容纳整个数组元素的,因此我们需要开辟第二个子数组来存储剩下的数组元素
        cnt = cnt + 1 = 2
        10 < 21
        10 + 8 < 21
        我们发现,两个子数组可以将整个数组元素放入,而 cnt 刚好等于 m,因此 [7,2,5] 和 [10,8] 就是分割出来的两个子数组,最小的最大值为 18

        为什么是放入元素直到放不下为止?因为要求的是连续子数组,我们需要保证每个连续的子数组的元素和都尽可能的接近 21

        如果我们最终得到的 cnt > m,那么表示我们划分出太多的子数组,也就是意味着一个子数组的容量太少,我们需要再扩大容量,即 left = mid + 1,然后继续进行二分
        如果我们最终得到的 cnt < m,那么表示我们划分出太少的子数组,也就是意味着一个子数组的容量太大,需要减少容量,即 right = mid - 1
        */
    public int splitArray(int[] nums, int m) {

        int left = 0;
        int right = 0;
        for(int num : nums){
            left = Math.max(left, num);
            right += num;
        }
         //二分 logn,内部遍历数组 n,时间复杂度 O(nlogn)
        while(left < right){
            int cnt = 1;
            int mid = (left + right) >>> 1;
            int sum = 0;
            for(int num : nums){
                if(sum + num > mid){
                    sum = 0;
                    cnt++;
                }
                sum += num;
            }
            if(cnt > m){
                left = mid + 1;
            }else{
                right = mid;
            }
        }
        return left;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 总结

  • 分析出几种情况,然后分别对各个情况实现
微信 支付宝
最近更新
01
1633. 各赛事的用户注册率 Java
07-02
02
1636. 按照频率将数组升序排序 Java
07-02
03
1639. 通过给定词典构造目标字符串的方案数 Java
07-02
更多文章>
Theme by Vdoing | Copyright © 2019-2025 JavaInterview.cn
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式