二分法的例题 —— 410. 分割数组的最大值(Kotlin)

二分法的例题 —— 410. 分割数组的最大值(Kotlin)_第1张图片

separatorLine

题目描述

分割数组的最大值

给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

注意:
数组长度 n 满足以下条件:

1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:

输入:
nums = [7,2,5,10,8]
m = 2

输出:
18

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

解决方案

class Solution {
    fun splitArray(nums: IntArray, m: Int): Int {
        // 由于是非负整数组,所以最小值为1,用Long保存的原因是防止数字越界
        var left = 1L
        // 最大值为总和+1 的作用是保证结果在left和right范围内,否则left可能会一直扩大,导致ans无法赋值
        var right = 1L
        nums.forEach {
            right += it
        }
        // 二分求解
        var ans = 0L
        while (left < right) {
            val middle = (left + right) / 2
            if (canSplit(nums, m, middle)) {
                // 可行,记录答案,缩小数组总和
                ans = middle
                right = middle
            } else {
                // 不可行,尝试更大的数组总和
                left = middle + 1
            }
        }
        return ans.toInt()
    }

    /**
     * 判断nums能否分成不大于maxCount组,且每组总和不大于maxSum的数组
     */
    private fun canSplit(nums: IntArray, maxCount: Int, maxSum: Long): Boolean {
        // 记录当前分组的总和
        var sum = 0L
        // 记录当前已分组的数量
        var count = 0
        // 尝试分组
        for (num in nums) {
            // 如果单个数字大于maxSum,无法分割出和小于maxSum的一组
            if (num > maxSum) return false
            // 如果和超过了maxSum,另起一组
            if (sum + num > maxSum) {
                count++
                sum = num.toLong()
            } else {
                // 贪心策略,一组中放的数字尽可能多
                sum += num
            }
        }
        // count < maxCount 而不是 count <= maxCount 的原因是:分割完成后,sum中的数字是一组,没计入count中
        return count < maxCount
    }
}

解题思路

1.本题的单调关系关系比较隐蔽,单调关系是:数组和的最大值越小,分组数越大。数组和的范围是可以确定的。

2.根据单调关系,将题目转换为:当子数组的和最大为maxSum时,至少需要分多少组,能否在最多m组的限制范围内完成分割。

3.由于是非空非负整数数组,最小值可设为1;数组和的最大值设置为nums的总和+1,+1的作用是保证结果在最小值和最大值范围内。采用Long保存中间值的作用是防止数组越界。

4.canSplit中用到了贪心策略,在子数组中尽可能多的放置元素,直到放不下,另起一组。

5.如果当前值满足分割条件,记录当前值,利用二分法,缩小子数组总和。否则扩大子数组总和,直到找到最佳答案。
separatorLine

推荐阅读:
我的LeetCode题解,使用kotlin语言:

二分法的例题 —— 410. 分割数组的最大值(Kotlin)_第2张图片

二分法的例题 —— 410. 分割数组的最大值(Kotlin)_第3张图片

注:本文排版仿照LeetCode官方公众号,侵删

你可能感兴趣的:(LeetCode题解)