洛谷P1182 数列分段(二分+贪心)

题目描述
对于给定的一个长度为N的正整数数列 A_{1\sim N}A
1∼N

,现要将其分成 MM(M\leq NM≤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,MN,M。

第 22 行包含 NN 个空格隔开的非负整数 A_iA
i

,含义如题目所述。

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

输入输出样例
输入 #1 复制
5 3
4 2 4 5 1
输出 #1 复制
6
题目分析:
一般像这种确定什么值是最合适的且答案取值范围已知的题都可以二分求解,二分枚举每个答案。
这个题答案的范围也很明确,肯定是从数列中最大值开始到数列所有元素和结束。那么我们就开始从最中间的值开始尝试,如果为了满足这个答案,分出来的区间数多于给定区间数,那么就说明这个答案太小了,必须尝试更大的数,因为只有数大一些才能使区间数减少。反之,如果为了满足这个答案,开出来的区间数过少,不够给定的区间数,那么就说明这个答案太大了,那么我们就必须去尝试更小的数,因为只有这样开出来的区间数才会增多。
代码

#include<iostream>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
bool judge(int x)
{
   int sum=0,qu=0;
   for(int i=1;i<=n;i++)
   {
       if(sum+a[i]<=x) sum+=a[i];
       else sum=a[i],qu++;
   }
   if(qu>=m) return true;
   else return false;
}
int main()
{
    cin>>n>>m;
    int right=0,left=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        right+=a[i];
        left=max(left,a[i]);
    }
    int mid;
    while(left<=right)
    {
        mid=(left+right)/2;
        if(judge(mid)) left=mid+1;
        else right=mid-1;
    }
    cout<<left;
    return 0;
}
发布了59 篇原创文章 · 获赞 58 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/amazingee/article/details/104706596