B. 【入门】分割数组的最小值

    传统题 100ms 64MiB

【入门】分割数组的最小值

题目描述

给定一个非负整数数组 aa 和一个整数 kk ,你需要将这个数组分成 kk 个非空的连续子数组。

设计一个算法使得这 kk 个子数组各自和的 最小值最大

  • 提示:这是第 2 类二分问题的模板,不熟悉的同学可以参考:
老师的模板
#include <bits/stdc++.h>
using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int n, k;
	cin >> n >> k;
	vector<int> a(n);
	for (int i = 0; i < n; i++) cin >> a[i];
	
	// 高级语法: 可以理解为申明一个函数 bool ok(int mid) { }
	auto ok = [&](int mid) {
		int d = 0;
		for (int i = 0; i < n; i++) {
			int sum = a[i];
			
			// 在和小于过 mid 的前提下,每个子段尽可能选少的数
			while (sum < mid && i + 1 < n) sum += a[++i];
			if (sum < mid) return false;  // 如果 sum < mid
			if (++d >= k) return true;    // 如果已经分成了 k 组
		}
		return d >= k;  // 分的段数要 >= k 才行
	};
	
	// 二分枚举答案, 这里求最小值最大, 注意边界
	// 确定边界, l = 0, r 最大为数组的和
	// accumulate(): 把 a.beign() 到 a.end() 的和累加到 0, 就是求和
	int l = 0, r = accumulate(a.begin(), a.end(), 0);
	while (l != r) {
		int mid = (l + r + 1) / 2;  // 注意:第 1 类二分, 这里需要 + 1
		if (ok(mid)) l = mid;       // mid 满足条件
		else r = mid - 1;           // mid 不满足条件
	}
	cout << l << '\n';

	return 0;
}

输入格式

输入包含 22

11 行包 22 个整数 n,kn, k,表示数组的长度和要分割的数量。

22 行包含 nn 个整数,用空格分开,表示数组 aa

输出格式

输出 kk 个子数组各自和的最小值的最大值

5 2
7 2 5 10 8
14
5 2
1 2 3 4 5
6
3 3
1 4 4
1

提示

【样例解释 1】

  • 一共有 44 种方法将 aa 分割为 22 个子数组。
  • 其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
  • 因为此时这两个子数组各自的和的最小值为14,在所有情况中最大。
请思考后再点击查看提示
  • 答案满足单调性,考虑二分枚举答案
  • 要求的是最小的答案,注意二分的边界情况

数据规模与限制

  • 1s.length103,0ai1061 \le s.length \le 10^3, 0 \le a_i \le 10^6
  • 1kmin(50,a.lenght)1 \le k \le min(50, a.lenght)