当前位置:   article > 正文

洛谷P1182数列分段

洛谷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;

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

(代码不太美观)

整体代码如下:

  1. //数列分段
  2. #include "iostream"
  3. using namespace std;
  4. int n,m,all;
  5. int num[100005];
  6. bool check(int x){
  7. int now=0,cnt=0;
  8. while(now<n){
  9. int sum=0;
  10. now++;
  11. sum+=num[now];
  12. if(sum>x)return false;
  13. while(sum<x&&now<n){
  14. now++;
  15. if(sum+num[now]<=x)sum+=num[now];
  16. else {
  17. now--;
  18. break;
  19. }
  20. if(sum==x)break;
  21. }
  22. cnt++;
  23. if(cnt>m)return false;
  24. }
  25. if(cnt<=m)return true;
  26. return false;
  27. }
  28. int binary(){
  29. int l=0,r=all+1;
  30. while(l+1<r){
  31. int mid=(l+r)>>1;
  32. if(check(mid))r=mid;
  33. else l=mid;
  34. }
  35. return r;
  36. }
  37. int main(){
  38. cin>>n>>m;
  39. for(int i=1;i<=n;i++){
  40. cin>>num[i];
  41. all+=num[i];
  42. }
  43. int ans=binary();
  44. cout<<ans;
  45. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号