给定一个非负整数数组 nums 和一个整数 k ,你需要将这个数组分成 k 个非空的连续子数组,使得这 k 个子数组各自和的最大值 最小。
返回分割后最小的和的最大值。
子数组 是数组中连续的部份。
示例 1:
输入:nums = [7,2,5,10,8], k = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。
其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
示例 2:
输入:nums = [1,2,3,4,5], k = 2
输出:9
示例 3:
输入:nums = [1,4,4], k = 3
输出:4
二分查找+贪心
class Solution {
public:bool check(vector<int>&nums, int x, int m){long long sum = 0;int cnt = 1;for(int i = 0; i < nums.size(); i++){if(sum + nums[i] > x){cnt++;sum = nums[i];}else{sum += nums[i];}}return cnt <= m;}int splitArray(vector<int>& nums, int k) {long long left = 0, right = 0;for(int i =0; i < nums.size(); i++){right += nums[i];left = max(left, (long long)nums[i]);}while(left < right){long long mid = (left + right) >> 1;if(check(nums, mid, k)){right = mid;}else{left = mid+1;}}return left;}
};
时间复杂度:O(n×log(sum−maxn)),其中 sum 表示数组 nums 中所有元素的和,maxn 表示数组所有元素的最大值。每次二分查找时,需要对数组进行一次遍历,时间复杂度为 O(n),因此总时间复杂度是 O(n×log(sum−maxn))。
空间复杂度:O(1)。
这道题的目的就是要找出连续子数组的最大值,那么我们可以思考,我们是不是可以假设一个最大值,然后看一下如果要让子数组分割的最大值不超过假定的最大值,那么要分割几次。于是我们就可以在check函数中使用贪心的思路来计算出要满足假定的最大值,最少需要分割cnt次。
一旦分割的次数小于等于题目要求的k的话,那么就说明假定的最大值大于等于实际的最大值,那么就让right = mid,这样才可以让接下来假定的最大值变小。反过来,如果cnt大于k,那么就要让left = mid + 1,让接下来的mid变大。
当left = right的时候,就说明假定的最大值就是实际的最大值。