题目描述 :给定一个非负整数数组 nums 和一个整数 m,将数组分成 m 个连续子数组,使得这 m 个子数组各自和的最大值最小。输出这个最小值。
输入:
-
第一行:
n m(1 ≤ n ≤ 10^5,1 ≤ m ≤ n) -
第二行:
n个非负整数
输出:最小的最大子数组和。
示例:
text
输入:
5 2
7 2 5 10 8
输出:
18
解释:分成 [7,2,5] 和 [10,8],最大和为 18。
解题思路
二分答案:
-
下界
lo = max(nums),上界hi = sum(nums)。 -
对于候选值
mid,贪心判断能否用不超过m个子数组使每个子数组和 ≤mid。 -
若能,说明答案可以更小,
hi = mid;否则lo = mid + 1。
参考代码
c
#include <stdio.h>
int canSplit(int *nums, int n, int m, long long limit) {
int cnt = 1;
long long sum = 0;
for (int i = 0; i < n; i++) {
if (sum + nums[i] > limit) {
cnt++;
sum = nums[i];
if (cnt > m) return 0;
} else {
sum += nums[i];
}
}
return 1;
}
int main(void) {
int n, m;
scanf("%d %d", &n, &m);
int nums[100005];
long long lo = 0, hi = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &nums[i]);
if (nums[i] > lo) lo = nums[i];
hi += nums[i];
}
while (lo < hi) {
long long mid = lo + (hi - lo) / 2;
if (canSplit(nums, n, m, mid))
hi = mid;
else
lo = mid + 1;
}
printf("%lld\n", lo);
return 0;
}