P1182 数列分段 Section II(二分)
题目描述
对于给定的一个长度为 N 的正整数数列 A1∼N,现要将其分成 M(M≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列 4 2 4 5 1 要分成 3 段。
将其如下分段:
[4 2][4 5][1]
第一段和为 6,第 2 段和为 9,第 3 段和为 1,和最大值为 9。
将其如下分段:
[4][2 4][5 1]
第一段和为 4,第 2 段和为 6,第 3 段和为 6,和最大值为 6。
并且无论如何分段,最大值不会小于 6。
所以可以得到要将数列 4 2 4 5 1 要分成 3 段,每段和的最大值最小为 6。
输入格式
第 1 行包含两个正整数 N,M。
第 2 行包含 N 个空格隔开的非负整数 Ai,含义如题目所述。
输出格式
一个正整数,即每段和最大值最小为多少。
#include <iostream>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n, m, a[N];
bool check(int x) {
int s = 0, num = 0;
for (int i = 1; i <= n; i++) {
if (s+a[i]<=x){
s+=a[i];
} else{
s=a[i];
num++;
}
}
return num>=m;
}
int main() {
cin >> n >> m;
int l = 0, r = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
l = max(l, a[i]);
r += a[i];
}
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) l = mid + 1;
else r = mid - 1;
}
cout << l;
return 0;
}
l = max(l, a[i]):l 是数组最大元素(答案下限)。
r += a[i]:r 是数组总和(答案上限)。
二分查找:
- if (check(mid)):
- true:mid 太小(份数太多),加大范围(l = mid + 1)。
- false:mid 太大(份数不够),缩小范围(r = mid - 1)。
check 函数:
检查如果每份和最多是 x,数组会被分成几份,返回份数是否 ≥ m。
- 从 a[1] 到 a[n] 遍历。
- 如果 s + a[i] ≤ x,就把 a[i] 加到当前份(s += a[i])。
- 如果超过 x,就新开一份(s = a[i]),份数加 1(num++)。