数列分段Section II

探讨了如何将一个正整数数列最优地分成指定数量的连续子序列,目标是最小化各子序列和的最大值。通过二分查找算法,确定了数列分割的最佳阈值,实现了效率与效果的平衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目描述】

对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列4 2 4 5 1要分成3段

将其如下分段:

[4 2][4 5][1]

第一段和为6,第2段和为9,第3段和为1,和最大值为9。

将其如下分段:

[4][2 4][5 1]

第一段和为4,第2段和为6,第3段和为6,和最大值为6。

并且无论如何分段,最大值不会小于6。

所以可以得到要将数列4 2 4 5 1要分成3段,每段和的最大值最小为6。

【输入】

第1行包含两个正整数N,M,第2行包含N个空格隔开的非负整数A[i],含义如题目所述。

【输出】

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

【输入样例】

5 3
4 2 4 5 1

【输出样例】

6

题解:

最小的部分和最大值一定是存在的(以下称其为“这个值”),只是我们还不知道。我们可以采用二分的方法逼近这个值,因为我们可以很容易地判断一个数是小于这个值还是大于这个值。 
如果一个数比这个值小,那么无论如何怎么分,也不可能把数列分成m段,使得部分和的最大值小于等于该数。因为这个值本身就是将数列分成m段其中部分和的最小值。也就是说,不能成功划分数列的的数是比这个值小的。 
如果一个数比这个值大,那么它就可以将数列分成m段,使得部分和的最大值小于等于该数。反过来说,能成功划分数列的数是大于这个值的数 

#include<cstdio>
#include<iostream>
using namespace std;
int a[100010],sum[100010];
int n,m;
bool check(int max_sum)//是否存在一种划分方法,使得部分和的最大值小于等于max_sum
{
    //初始化
    int sum = 0;
    int sum_section = 0;
    for(int i = 1; i <= n; ++i)
    {
        sum += a[i];//累加
        if(sum > max_sum)//如果超了
        {
            sum = a[i];//就赋成a[i]
            sum_section++;//段数+1
        }
    }
    sum_section++;//无论加上最后一个元素超出max_sum还是不超出,都要将段数+1
    if(sum_section <= m)//如果段数小于等于m
        return true;//可以,仔细想想这个逻辑
    else//否则不可以
        return false;
}
int main()
{
    cin >> n >> m;//输入
    int max_a = -1;//初始化
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d",&a[i]);
        sum[i] = sum[i - 1] + a[i];//前缀和
        max_a = max(max_a,a[i]);//数列中的最大值
    }
    int l = max_a,r = sum[n];//二分边界,当分成n段时,显然部分和的最大值就是max_a,当分成的段数小于n时,部分和的最大值开始增大,当分成1段时,部分和的最大值达到最大,即为数列的前n项和。如此分析,便得出了二分的边界
    while(l < r)//二分模板
    {
        int mid = (l + r) / 2;//注意最大值最小化是这样的,而最小值最大化是(l + r + 1) / 2;
        if(check(mid) == true)
            r = mid;//保证答案的正确性
        else
            l = mid + 1;
    }
    cout << r << endl;//输出r
    return 0;
}

 

 


--------------------- 
原文:https://blog.youkuaiyun.com/suntengnb/article/details/78045645 
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值