【C++】「一本通 1.2 练习 1」数列分段 II

【来源】

一本通题库-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 NM

第 2 行包含 N N N 个空格隔开的非负整数 A i A_i Ai,含义如题目所述。

【输出格式】

仅包含一个正整数,即每段和最大值最小为多少。

【样例输入】

5 3
4 2 4 5 1

【样例输出】

6

【数据范围】

对于 20 % 20\% 20% 的数据,有 N ≤ 10 N≤10 N10

对于 40 40% 40 的数据,有 N ≤ 1000 N≤1000 N1000

对于 100 100% 100 的数据,有 N ≤ 1 0 5 N≤10^5 N105 M ≤ N M≤N MN 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;
}

猜你喜欢

转载自blog.csdn.net/Ljnoit/article/details/105649845