这道题本质上是用到了二分+贪心。
先介绍一下贪心。贪心解决的是局部最优解问题,但是如果一个问题每一个的局部的最优解对下一个局部不造成影响,显然这样就成为了全局最优解。那么对于此题是否适用呢?显然,这个题的最优解a是数列中的最大值。而最糟糕的情况b是只能划分为一段,这样就是数列之和。我们不妨从a枚举到b,考虑每一个值是否可行。那么判断可行的条件是什么呢?我们以输入样例为例。4,2,4,5,1。如果选择7,2和4之间要划分一次,4和5之间要划分一次,至少划分2次。这样意味着可以进一步划分,而进一步划分的结果是让每一段和不变大,甚至还能变小。反之,如果划分次数大于要求,也就只能让枚举递增。
不过,这样显然效率太低。根据上面的分析,我们显然可以用二分法解决问题。
这样其实已经可以解决问题了。不过每一次判断累加有些繁琐,这样我们就可以用前缀和解决。还以样例为例,4,2,4,5,1。我们改动一下。0,4,6,10,15,16。每一位是前面加上自身的和。这样,我们求每一段之和,就可以直接作差相减。比如第2位数加上第3位数,就可以表示为10-4。
代码如下:
#include<iostream>
using namespace std;
int a[100001];
int N;
int c;
int max(int a, int b) {
return a > b ? a : b;
}
bool check(int x) {
int o = 0;
for (int i = 1, j = 0; i <= N; i++) {
if (a[i] - a[j] > x) {
o++;
j = i - 1;
i--;
}
}
return o <= c - 1;
}
int main()
{
int l = 0;
a[0] = 0;
cin >> N >> c;
for (int i = 1; i <= N; i++) {
int t;
cin >> t;
a[i] = a[i - 1] + t;
l = max(l, t);
}
int r = a[N];
while (l < r) {
int mid = l + (r - l) / 2;
if (check(mid))
r = mid;
else l = mid + 1;
}
cout << l;
return 0;
}