对于给定的一个长度为N的正整数数列 A_{1\sim N}A1∼N,现要将其分成 MM(M\leq NM≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列 4\ 2\ 4\ 5\ 14 2 4 5 1 要分成 33 段。
将其如下分段:
[4\ 2][4\ 5][1][4 2][4 5][1]
第一段和为 66,第 22 段和为 99,第 33 段和为 11,和最大值为 99。
将其如下分段:
[4][2\ 4][5\ 1][4][2 4][5 1]
第一段和为 44,第 22 段和为 66,第 33 段和为 66,和最大值为 66。
并且无论如何分段,最大值不会小于 66。
所以可以得到要将数列 4\ 2\ 4\ 5\ 14 2 4 5 1 要分成 33 段,每段和的最大值最小为 66。
输入格式
第 11 行包含两个正整数 N,MN,M。
第 22 行包含 NN 个空格隔开的非负整数 A_iAi,含义如题目所述。
输出格式
一个正整数,即每段和最大值最小为多少。
输入输出样例
输入 #1复制
5 3 4 2 4 5 1
输出 #1复制
6
说明/提示
对于 20\%20% 的数据,N\leq 10N≤10。
对于 40\%40% 的数据,N\leq 1000N≤1000。
对于 100\%100% 的数据,1\leq N\leq 10^51≤N≤105,M\leq NM≤N,A_i < 10^8Ai<108, 答案不超过 10^9109。
题解:
对于这种求最大值最小,最小值最大这类题,看到这类字眼就要想到二分。
二分界限[数列最大值,数列之和],保证每段都可以分,最多分成一整段。
那么check(x),相当于检查当每组的最大值不超过x时,能否把它分成要求的m组,能的话就继续缩小x的范围。
如何check(x),按顺序考虑即可,sum+=每一个数,若超过x,就新开一组存,否则就在sum中存,最后判断一下组数是否超过要求的m组即可
完整代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int f[N];
int n, m;
bool check(int x){
int cnt = 0, sum = 0;
for(int i=0;i<n;i++) {
if(sum+f[i]<=x){
sum+=f[i];
}
else {
sum = f[i];
cnt++;
}
}
return cnt<m;
}
int main(){
cin>>n>>m;
int l = 0, r = 0;
for(int i=0;i<n;i++) {
scanf("%d",&f[i]);
l = max(l,f[i]);
r += f[i];
}
while(l<r){
int mid = l+r>>1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout<<l<<endl;
return 0;
}