5.9每日一题题解

数列分段 Section II

涉及知识点:

  • 二分/思维

solution

  • \(我们可以看到它让求的是最大值最小问题,诸如最大值最小,最小值最大这种问题,我们都可以使用二分答案来求解。\)
  • \(即然要使用二分,我们就需要解决两个问题:\)
  • \(1. 二分的左右边界是什么?\)
  • \(左边界表示答案的最小取值,很显然,它最小的情况下只能是所给数的最大值,所以左边界取数列的最大值\)
  • \(右边界表示答案的最大取值,由于本题说明给出的数都为正整数,所以只要把所有数相加就是右边界\)
  • \(2. 二分的判断条件是什么?\)
  • \(我们可以假设当前的mid为我们的答案,然后按照条件判断。\)
  • \(从头按照将mid看成最大值来分区域,如果划分的区域数小于等于规定的区域数则为真,否则为假。\)

代码

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e5+10;

int n,m;
int a[N];
int l=-1,r=0;

bool check(int x)
{
    int i = 0;//表示当前遍历到第几个数
    int section = 1;//以x为最大值划分的区域个数,初始为1
    int sum = 0;//表示当前区域的和
    while (i <= n-1)//由于循环里无论结果怎样i都会加一,所以只要循环到n-1就可以完成1~n的循环
    {
        if(sum + a[i+1] <= x) sum += a[++i];//如果下一个数加上sum还没到x,则继续遍历
        else sum = a[++i],section++;//如果下一个数加上sum比x要大,则下一个数就是一个新的区域
    }
    if(section <= m) return true;//如果划分的区域数比m小,返回true
    else return false;//如果划分的区域数比m大,返回false
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1 ; i <= n ; i ++ )
    {
        scanf("%d",&a[i]);
        l = max(l,a[i]);//左边界取数列的最大值
        r = r + a[i];//右边界为数列的和
    }
    
    while (l < r)
    {
        int mid = l + r >> 1;
        if(check(mid)) r = mid;//如果划分的数量比m小,说明mid过大,则答案在mid左边或等于mid
        else l = mid+1;//如果划分的数量比m大,说明mid过小,则答案在mid右边
    }
    printf("%d\n",l);
    return 0;
}
posted @ 2020-05-09 08:03  QFNU-ACM  阅读(94)  评论(0编辑  收藏  举报