力扣【二分查找】:410. 分割数组的最大值
题目描述:
给定一个非负整数数组 nums 和一个整数 k ,你需要将这个数组分成 k 个非空的连续子数组,使得这 k 个子数组各自和的最大值 最小。
返回分割后最小的和的最大值。
子数组 是数组中连续的部分。
算法思路:
-
题目要求将数组划分为k个有序子数组,并且要求在限定k个子数组的情况下所有子数组各自的和当中的最大值要最小,很显然这个值也是有一个范围的,最小应该是数组的最大值,例如当子数组个数k等于数组长度n时,满足最小的最大值的就是nums的最大值,这个范围的最大不会超过数组的和,当k等于1时,唯一的子数组就是nums,因此满足要求的值就是nums元素的和。
-
由于这个区间天然的有序性,一旦找到能够依照“k个子数组各自和的最大值”划分数组得到子数组个数的方式就可以用二分查找的方法寻找满足要求的最小值,二分查找时只需要根据划分的子数组个数和k的比较结果调整区间范围即可。
-
这里的划分方式和LCP 12. 小张刷题计划的划分方式非常相似,由于要求非空连续子数组,我们可以遍历数组,记录每个子数组的和以及已统计的子数组个数,每当满足要求时做出相应更新即可。- - 由于需要判断当前的子数组和加上当前遍历的元素的和是否超过要求的最大数组和,我们同样可以用当前子数组和 + 遍历元素的方式判断是否满足要求,很显然当该值大于test_value时构成已统计和的那个子数组就应该做一次划分,当小于时则只需要将遍历元素加入子数组和即可(扩展了子数组的范围),重点在于当等于test_value时我们应该如何处理,这里其实可以将等于的情况合并到小于,这样就可以把更新逻辑统一到大于的情况处理,代码更简洁,而小于等于时就只需要更新求和值即可。
-
当当前子数组和 + 遍历元素的临时值大于要求测试的值时,只需要将子数组和重置为当前的遍历元素并增加一个子数组计数即可。
-
关于子数组的计数统计是每个数组开始还是结束的简单讨论:不论思考时认为是在开始或结束计数,都只需要考虑最后一个元素的情况,因为前面的子数组不论如何都一定完成了计数(只不过前置计数时初始化为1,后置计数初始化为0),首先考虑前置计数,此时初始化计数为1表示第一个子数组已经满足要求,此时如果整个数组都是第一个子数组,那么最后一个元素 + 子数组和一定小于等于target,此时不会更新数组计数,子数组个数为1,正确,而如果最后一个元素 + 子数组和大于target,此时子数组计数加1,这意味着把最后一个元素单独作为一个数组进行了统计,结果同样正确。然后考虑后置计数,此时计数初始化为0,同样考虑最后一个元素,如果最后一个元素 + 子数组和小于等于target,那么子数组计数不会加1,所以结束循环后需要再加1,而如果最后一个元素 + 子数组和大于target,子数组计数加1,且统计的意思是最后一个元素前的子数组,所以最后一个元素同样没有被单独计入一个子数组,同样需要循环结束加1。综上对于这类问题,前置计数和后置计数的区别仅仅是计数初始化和循环结束是否需要加1再返回结果的区别,本质可以看作将每次的计数统计到一个数组中,前置计数和后置计数的区别只是把这个数组做了一个横向一格的位移。
代码:
时间复杂度:设区间大小为m,数组长度为n,对区间的每次二分查找都需遍历一次数组,O(n log (m))
空间复杂度:O(1)
class Solution {
public:
int splitArray(vector<int>& nums, int k) {
int lower_bound = *std::max_element(nums.begin(), nums.end()),
upper_bound = std::accumulate(nums.begin(), nums.end(), 0);
while (lower_bound < upper_bound) {
int test_value = lower_bound + (upper_bound - lower_bound) / 2;
int groups = GetGroups(nums, test_value);
if (groups > k) {
lower_bound = test_value + 1;
} else {
upper_bound = test_value;
}
}
return upper_bound;
}
int GetGroups(std::vector<int> &nums, int test_value) {
int group_count = 1;
int group_sum = 0;
for (const int &num : nums) {
if (group_sum + num > test_value) {
++group_count;
group_sum = num;
} else {
group_sum += num;
}
}
return group_count;
}
};
浙公网安备 33010602011771号