問題描述:
給定一個非負整數數組和一個整數 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,在所有情況中最小。
基本思路:
leetcode上難度爲困難的題目。用到了很巧妙的二分思想。
以前我們的二分都是在已有的數組中尋找元素,這裏的二分則不同。
它是預測你的連續子數組的最大值,來分隔你的數組。根據分割的結果+二分的思想來重新預測。
代碼上看十分簡單。
這體現了二分的本質:預測+修改預測。這個預測不一定要是數組中的索引啊啥的,可以是像本題中的元素的和。
AC代碼:
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
// 使用抽象的夾逼二分求
long long left = INT_MIN; // 看來以後要特別注意類型了,全部改成long long
long long right = 0;
for (long long num : nums) {
left = max(num, left);
right += num;
}
while (left < right) {
long long mid = left + (right - left) / 2;
// 看看最大值爲mid的時候能分成幾對
int count = GetM(nums, mid);
// 發現當前的mid只能夠分count的數太小,說明mid偏大了
if (count <= m) right = mid;
else left = mid + 1;
}
return left;
}
int GetM(vector<int> &a, int n) {
// 貪心法分數組
int count = 1;
long long cur = 0;
for (int num : a) {
cur += num;
if (cur > n) {
++count;
cur = num;
}
}
return count;
}
};