一本通题解——1436:数列分段II

本文介绍了一种利用二分查找算法解决数列最优分段的问题,旨在找到给定数列分段后的每段和的最大值最小的情况。通过实例演示了二分查找过程,详细解释了算法思路与实现代码。

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

题目链接

一本通OJ:http://ybt.ssoier.cn:8088/problem_show.php?pid=1436

我的OJ:http://47.110.135.197/problem.php?id=4461

题目

题目描述

对于给定的一个长度为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

数据范围

N ≤ 1000,000,M ≤ N,1 ≤ Ai ≤ 1000000。

P.S. 一本通 OJ 是没有给出数据范围,我是经过多次测试出的数据范围,主要是 N 的大小。

分析

题意分析

从题目的描述中,我们可以知道,需要从 max(A[i]) 到 \sum_{i=1}^{N}A[i] 中找到一个合适的长度,对数列 A 进行分段。自然想到使用二分查找。

使用输入样例,我们来模拟一下二分查找的过程。

leftrightmid分段情况分析
51610[4 2 4] [5 1]不满足题目要求,mid 偏大,要变小
597[4 2] [4] [5 1]满足题目要求,mid 可能偏小,要变大
565[4] [2] [4] [1]满足题目要求,mid 可能偏小,要变大
666[4 2] [4] [5 1]满足题目要求,mid 可能偏小,要变大
65结束二分查找,最佳的答案是 6 。

算法思路

一个标准的二分查找模板。

二分查找

1、left = max(A[i]),right = sum(A[1], A[2], ......, A[n]) 这个数据范围,进行二分查找

2、以 mid 为每段和的最大值,也就是每次分段和都不能超过 mid。

3、如果分段数 cnt > m,说明这个 mid 偏小,那么 mid 还可以再大一点,也就是左边界(left)移动到 mid+1;如果分段数 cnt ≤ m,说明这个 mid 偏大,那么 mid 还可以再小一点,也就是右边界(right)移动到 mid-1。注意需要记录下这个 mid。

AC参考代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e6+2;
int data[MAXN];

bool check(int x, int n, int m) {
    int cnt=1;
    int len=0;//当前长度
    for (int i=1; i<=n; i++) {
        if (len+data[i]<=x) {
            len += data[i];
        } else {
            cnt++;
            len = data[i];
        }
    }

    return cnt<=m;
}

int main() {
    int n,m;
    cin>>n>>m;

    int i;
    int left=0;
    int right=0;
    for (i=1; i<=n; i++) {
        cin>>data[i];
        left = max(left, data[i]);
        right += data[i];
    }

    int mid;
    int ans;
    while (right>=left) {
        mid = left+((right-left)>>1);

        //按照mid为最大值分段进行分段处理
        if (true==check(mid, n, m)) {
            //满足题目要求,mid 可能偏小,要变大
            ans = mid;//记录下这个分段值
            right = mid-1;
        } else {
            //不满足题目要求,mid 偏大,要变小
            left = mid+1;
        }
    }

    cout << ans << endl;

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值