题解:洛谷 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
【解题思路】

【算法标签】
《洛谷 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
浙公网安备 33010602011771号