【来源】
一本通题库-1436
LibreOJ-10014
vjudge
【题目描述】
对于给定的一个长度为 N N N 的正整数数列 A A A ,现要将其分成 M M M 段,并要求每段连续,且每段和的最大值最小。
例如,将数列 4 2 4 5 1 要分成 3 段:
若分为 [ 4 2 ] [ 4 5 ] [ 1 ] [4\ 2][4\ 5][1] [4 2][4 5][1],各段的和分别为 6,9,1 ,和的最大值为 9;
若分为 [ 4 ] [ 2 4 ] [ 5 1 ] [4][2\ 4][5\ 1] [4][2 4][5 1],各段的和分别为 4,6,6 ,和的最大值为 6;
并且无论如何分段,最大值不会小于 6。
所以可以得到要将数列 4 2 4 5 1 要分成 3 段,每段和的最大值最小为 6 。
【输入格式】
第 1 行包含两个正整数 N , M N,M N,M;
第 2 行包含 N N N 个空格隔开的非负整数 A i A_i Ai,含义如题目所述。
【输出格式】
仅包含一个正整数,即每段和最大值最小为多少。
【样例输入】
5 3
4 2 4 5 1
【样例输出】
6
【数据范围】
对于 20 % 20\% 20% 的数据,有 N ≤ 10 N≤10 N≤10;
对于 40 40% 40 的数据,有 N ≤ 1000 N≤1000 N≤1000;
对于 100 100% 100 的数据,有 N ≤ 1 0 5 N≤10^5 N≤105, M ≤ N M≤N M≤N, A i A_i Ai 之和不超过 1 0 9 10^9 109 。
【解析】
二分。
记录最大的数还有所有数总和,答案在这段区间内二分。
之后判断答案是否符合要求。根据划分的段数<m,即答案可以再小。
【代码】
#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define RI register int
#define re(i,a,b) for(RI i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
const int N=1e5+5;
int n,m,total,t;
int a[N];
bool check(int x,int a[]) {
for(int i=1; i<=n; i++) {
if(total+a[i]<=x) total+=a[i];
else {
total=a[i];
t++;
}
}
return t>=m;
}
int main() {
scanf("%d%d",&n,&m);
int l=0,r=0;
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
r+=a[i];
l=l>a[i] ? l : a[i];
}
while(l<=r) {
int mid=(l+r)>>1;
total=0,t=0;
if(check(mid,a)) l=mid+1;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}