题解:洛谷 P1182 数列分段 Section II

【题目来源】

洛谷:P1182 数列分段 Section II - 洛谷

【题目描述】

对于给定的一个长度为 \(N\) 的正整数数列 \(A_{1\sim N}\),现要将其分成 \(M(M\le 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

【解题思路】

image

【算法标签】

《洛谷 P1182 数列分段》 #贪心# #二分# #前缀和#

【代码详解】

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

// 全局变量:
// n: 数字序列长度
// m: 最大分段数
// a[100005]: 存储数字序列
// l: 二分查找左边界(单个元素最大值)
// r: 二分查找右边界(所有元素总和)
int n, m, a[100005], l = 0, r = 0;

int main()
{
    // 输入序列长度和最大分段数
    cin >> n >> m;
    
    // 输入数字序列并确定二分查找边界
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (l < a[i]) l = a[i];  // 左边界取最大值
        r += a[i];               // 右边界为总和
    }
    
    // 二分查找最小化最大子段和
    while (l <= r)
    {
        int mid = l + (r - l) / 2;  // 计算中间值,防止溢出
        int count = 1;             // 初始分段数为1
        int tmp = 0;               // 当前子段和
        
        // 计算当前mid值下需要多少段
        for (int i = 1; i <= n; i++)
        {
            if (a[i] + tmp <= mid)  // 可以加入当前子段
            {
                tmp += a[i];
            }
            else  // 需要新开一段
            {
                count++;
                tmp = a[i];
            }
        }
        
        // 根据分段数调整搜索范围
        if (count <= m)  // 分段数不足或刚好,尝试更小的最大值
        {
            r = mid - 1;
        }
        else  // 分段数过多,需要更大的最大值
        {
            l = mid + 1;
        }
    }
    
    // 输出最终确定的最小化最大子段和
    cout << l << endl;
    
    return 0;
}

【运行结果】

5 3
4 2 4 5 1
6
posted @ 2026-02-17 19:04  团爸讲算法  阅读(3)  评论(0)    收藏  举报