题目链接🔗: leetcode.cn/problems/sp...
题目描述:

思路+代码:
ini
#include<iostream>
#include<vector>
#include<algorithm>
// 最大值最小化问题,同样二分答案,要最小化的最大值有上下界,
// 上界是 sum,下界是 nums 中的最大值,二分尝试这个范围里的每个
// 可能的答案,每一轮通过贪心计算要以当前的估计值作为划分后子数组
// 最大值的话,最少要划分成多少组才能满足,然后用这个计算结果和
// 真实的k作比较。首先要明白这里为什么贪心(这里也就是顺序遍历处理)能
// 实现局部最优到全局最优,首先子数组是连续的,其次第一组贪心策略放入的元素
// 数目一定不会少于其他策略放入的元素数目,后面的分组当然也是同样的道理,
// 换句话说,要在指定最大值限制下分成最少的组数,那我每组的元素当然有尽可能多放,不然留到
// 后面,肯定要占另外的组,也就有可能会造成要划分出新的组,增加了组数。
// 然后再看怎么二分,如果算出来的最小需要划分成的组数小于 k,那也就是
// 说要划分成更多的组,此时我们可以往更小估值,这样可能会有和值比较大的子数组
// 拆开,以使得组数增加; 如果算出来的最小需要划分成的组数等于 k,那就继续往小估,
// 因为有可能有更小的最大值也能满足最小需要组数等于 k, 注意这里只是可能,所以
// r 不能移到 mid - 1, 而是移到 mid; 如果算出来的最小需要划分成的组数大于 k,
// 便要通过增大估值,这样可能会有和值比较小的子数组被合并,以使得组数减少。
//
// 这里也做了小的优化,就是当当前估的最大值不是任何贪心划分出来的子数组和的时候,
// 我们在贪心的时候获取实际子数组中和的最大值,然后在 min_k<=k 同时 max_sum < m 的
// 时候直接 r = max_sum。
//
// 复杂度:O(nlog(sum))
std::vector<int> get_min_k(std::vector<int>&nums, int m){
int t = nums.size();
int count = 0, tem_sum = 0, max_sum = -1;
for(int i=0; i<t; i++){
if(tem_sum+nums[i]>m){
count ++;
max_sum = std::max(max_sum, tem_sum);
tem_sum = nums[i];
}
else{
tem_sum += nums[i];
}
}
return {count+1, std::max(max_sum, tem_sum)};
}
int splitArray(std::vector<int>& nums, int k) {
int t = nums.size(), res = 0;
int sum = 0, max_num = -1;
for(int i = 0; i<t; i++){
sum += nums[i];
max_num = std::max(max_num, nums[i]);
}
int l = max_num, r = sum;
while(l<r){
int m = (l+r)/2;
int min_k = get_min_k(nums, m)[0];
int max_sum = get_min_k(nums, m)[1];
if(min_k<=k) max_sum < m ? r = max_sum : r = m;
else l = m + 1;
}
return l;
}
int main(){
int n, a, k;
std::cin >> n;
std::vector<int> nums;
for(int i=0; i<n; i++){
std::cin >> a;
nums.push_back(a);
}
std:: cin >> k;
std:: cout << splitArray(nums, k);
return 0;
}