二分法的例題 —— 410. 分割數組的最大值(Kotlin)

head

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語言:

注:本文排版仿照LeetCode官方公衆號,侵刪

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章