题目描述
对于给定的一个长度为N的正整数数列A-iA−i,现要将其分成M(M≤N)M(M≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列4 2 4 5 142451要分成33段
将其如下分段:
[4 2][4 5][1][42][45][1]
第一段和为66,第22段和为99,第33段和为11,和最大值为99。
将其如下分段:
[4][2 4][5 1][4][24][51]
第一段和为44,第22段和为66,第33段和为66,和最大值为66。
并且无论如何分段,最大值不会小于66。
所以可以得到要将数列4 2 4 5 142451要分成33段,每段和的最大值最小为66。
扫描二维码关注公众号,回复:
5387054 查看本文章
输入输出格式
输入格式:
第11行包含两个正整数N,M。
第22行包含NN个空格隔开的非负整数A_iAi,含义如题目所述。
输出格式:
一个正整数,即每段和最大值最小为多少。
输入输出样例
输入样例#1: 复制
5 3 4 2 4 5 1
输出样例#1: 复制
6
#include<iostream>
#include<vector>
using namespace std;
int arr[100002];
int n, m;
int lef, rig, mid;
int tu, ts;
bool judge(int mid)//用来判别是否每一段都可以小于mid
{
for (int i = 1; i <= n; i++)
{
if (tu + arr[i]<= mid)//如果还是小于mid,就继续加上去
tu += arr[i];
else//否则就开启另一段,ts加一,并且设置tu为当前的数字
{
ts += 1;
tu = arr[i];
}
}
return ts >= m;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
rig += arr[i];
lef = lef > arr[i] ? lef : arr[i];
}
while (lef<=rig)
{
mid = (lef + rig) / 2;
ts = tu = 0;
if (judge(mid))//如果最后的ts是大于m的话,说明mid太小了,需要lef往右移
lef = mid + 1;
else//否则就是mid太大了
rig = mid - 1;
}
cout << lef << endl;
system("pause");
return 0;
}
解析:
最大值的最小值:二分的标志
我们的二分是从这个数列中的最大值到数列总和,为什么?
从最大值开始是因为这个序列最少一段的数量是一个数字,还要这一段数字最大,所以要从最大的数开始。到总和结束是因为小于总和的值都有可能是最后的答案,需要二分来判别
具体见注释