洛谷P1182数列分段

题目描述

对于给定的一个长度为 N 的正整数数列 A_{1-N},现要将其分成 M(M≤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,M。

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

输出格式

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

输入输出样例

输入 #1

5 3
4 2 4 5 1

输出 #1

6

说明/提示

对于 20%20% 的数据,N≤10。

对于 40%40% 的数据,N≤1000。

对于 100%100% 的数据,1≤N≤105,M≤N,Ai​<10^{8}, 答案不超过 10^{9}

        这道题有了之前的经验,就能看出用二分答案比较合适。

        对于二分答案,我想谈谈自己的理解。所谓二分答案,就是“二分”和“答案”两部分,答案是对象,二分是手段,采用二分的手法对答案进行高级枚举。而二分就可以作为一种能够有效降低时间复杂度的一种枚举方法。而二分答案的难点并非二分的函数,而是验证二分出来的mid能不能符合题意的函数,我一般命名为check函数。对于这道题,我想主要讲一下写check函数的具体思路。

关于check函数,具体思路如下(建议配合代码食用):

        1.对于check函数,我要传什么参?我们最终输出的答案是最大值的最小,所以二分答案是对这些值的二分,左限定为0,右限定为输入所有数的和+1,传给check函数就是每次二分后的结果,即(l+r)/2,在check函数中我们定义为x。

        2.首先我定义了两个函数,一个now,一个cnt,now的值是当前遍历数组的下标,cnt是将数组分的段数,最终判断x值是否符合条件也是根据cnt判断的。

        3.整体思路就是,用now指针从零开始遍历数组,用sum记录每一段的总和,保证不超过m,一旦超过,break跳出循环,继续下次遍历,每次对分段时cnt++,在整个过程中,一旦cnt>m,reruen false;

bool check(int x){
    int now=0,cnt=0;
    while(now<n){
        int sum=0;
        now++;
        sum+=num[now];
        if(sum>x)return false;//如果第一个值就已经大于约定的值x了,那么这个值是注定不可以的,直接return掉就好了
        while(sum<x&&now<n){//保证每段之和小于m的同时now指针不越界
            now++;
            if(sum+num[now]<=x)sum+=num[now];//先试探一下,看看加上下一个元素会不会超过约定值,如果不超过sum直接加上,继续下次循环,如果超过了,记得把now归位,并且跳出循环
            else {
                now--;
                break;
            }
            if(sum==x)break;//如果sum刚好等于x了,也break
        }
        cnt++;//每一个小循环注定要多一段
        if(cnt>m)return false;
    }
    if(cnt<=m)return true;
    return false;
}

(代码不太美观)

整体代码如下:

//数列分段
#include "iostream"
using namespace std;
int n,m,all;
int num[100005];
bool check(int x){
    int now=0,cnt=0;
    while(now<n){
        int sum=0;
        now++;
        sum+=num[now];
        if(sum>x)return false;
        while(sum<x&&now<n){
            now++;
            if(sum+num[now]<=x)sum+=num[now];
            else {
                now--;
                break;
            }
            if(sum==x)break;
        }
        cnt++;
        if(cnt>m)return false;
    }
    if(cnt<=m)return true;
    return false;
}
int binary(){
    int l=0,r=all+1;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid;
        else l=mid;
    }
    return r;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>num[i];
        all+=num[i];
    }
    int ans=binary();
    cout<<ans;
}

相关推荐

  1. 入门P1000-P1482题解

    2024-03-17 19:26:03       12 阅读
  2. 入门——P1152 欢乐的跳

    2024-03-17 19:26:03       18 阅读
  3. p1216数字三角形

    2024-03-17 19:26:03       39 阅读
  4. P8823

    2024-03-17 19:26:03       36 阅读
  5. P2863

    2024-03-17 19:26:03       16 阅读
  6. P3806 [模板] 点分治 1 题解

    2024-03-17 19:26:03       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-17 19:26:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-17 19:26:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-17 19:26:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-17 19:26:03       18 阅读

热门阅读

  1. (容斥原理例题)洛谷P1287 盒子与球

    2024-03-17 19:26:03       19 阅读
  2. LeetCode350:两个数组的交集Ⅱ

    2024-03-17 19:26:03       19 阅读
  3. 配置服务器自启动极简方式 /etc/rc.d/rc.local

    2024-03-17 19:26:03       21 阅读
  4. Kettle安装使用手册

    2024-03-17 19:26:03       15 阅读
  5. Linux 常用命令总结

    2024-03-17 19:26:03       22 阅读
  6. 如何查看设备树——设备树格式解析

    2024-03-17 19:26:03       22 阅读
  7. select定时器功能,c语言实现

    2024-03-17 19:26:03       23 阅读
  8. 算法-KMP匹配

    2024-03-17 19:26:03       18 阅读
  9. 【亲测可行】Mac上clion boost库的安装与使用

    2024-03-17 19:26:03       18 阅读