二分最大段和,用从左到右尽量装满当前段的贪心检查最少段数。
OJ: luogu
题目 ID: P1182
难度:普及/提高-
标签:二分答案贪心python
日期: 2026-06-22 20:57
题意
给定一个正整数序列,要把它分成 M 段,每段连续。
要求最小化所有段中最大的段和。
思路
先看一个可以直接验证想法的朴素解:
cpp
// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const long long INF = (1LL << 60);
int n, m;
long long a[MAXN], prefix_sum[MAXN];
long long dp[MAXN][MAXN];
long long range_sum(int l, int r) {
return prefix_sum[r] - prefix_sum[l - 1];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
prefix_sum[i] = prefix_sum[i - 1] + a[i];
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
dp[i][j] = INF;
}
}
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= m; k++) {
for (int p = 0; p < i; p++) {
if (dp[p][k - 1] == INF) {
continue;
}
dp[i][k] = min(dp[i][k], max(dp[p][k - 1], range_sum(p + 1, i)));
}
}
}
cout << dp[n][m] << '\n';
return 0;
}暴力 DP 可以枚举分段点,但数据范围更适合二分答案。
设答案上限为 limit。我们只需要判断:能否把序列分成不超过 M 段,并且每段和都不超过 limit。
检查方法很简单:从左到右扫描,当前段能放就继续放;如果再放会超过 limit,就新开一段。
这样得到的是在 limit 限制下的最少段数。如果最少段数不超过 M,说明 limit 可行;否则不可行。
可行性随 limit 增大而单调变好,所以可以二分最小可行值。
Python 知识
left = max(numbers)、right = sum(numbers)直接给出答案的紧二分边界。- 判定函数按值遍历列表,用
current_sum和segments表达当前段与已用段数。 - 正整数保证“当前段尽量装满”能得到固定上限下的最少段数。
/home/rainboy/mycode/hugo-blog/content/program_language/python/generator_expression.md:聚合函数与可迭代对象的使用边界。/home/rainboy/mycode/hugo-blog/content/program_language/python/oj_input_output_cheatsheet.md:大量整数 token 读取。
代码
python
import sys
data = list(map(int, sys.stdin.buffer.read().split()))
n, segment_limit = data[:2]
numbers = data[2:2 + n]
def possible(maximum_sum):
segments = 1
current_sum = 0
for number in numbers:
if current_sum + number <= maximum_sum:
current_sum += number
else:
segments += 1
current_sum = number
return segments <= segment_limit
left, right = max(numbers), sum(numbers)
while left < right:
middle = (left + right) // 2
if possible(middle):
right = middle
else:
left = middle + 1
print(left)复杂度
每次检查为
text
O(n log sum)空间复杂度为
总结
本题是典型的“最大值最小”二分答案。
关键是固定答案后用贪心检查最少段数:每段尽量往右扩展,不会让后面更难分,因此这个检查是正确的。