对于给定的一个长度为 N N N 的正整数数列 A 1 − N A_{1−N} A1−N,现要将其分成 M ( M ≤ N ) M(M≤N) M(M≤N) 段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列 4 , 2 , 4 , 5 , 1 4,2,4,5,1 4,2,4,5,1 要分成 3 3 3 段。
将其如下分段: [ 4 , 2 ] [ 4 , 5 ] [ 1 ] [4,2][4,5][1] [4,2][4,5][1]。 第一段和为 6 6 6,第 2 2 2 段和为 9 9 9,第 3 3 3 段和为 1 1 1,和最大值为 9 9 9。
将其如下分段: [ 4 ] [ 2 , 4 ] [ 5 , 1 ] [4][2,4][5,1] [4][2,4][5,1]。第一段和为 4 4 4,第 2 2 2 段和为 6 6 6,第 3 3 3 段和为 6 6 6,和最大值为 6 6 6。
并且无论如何分段,最大值不会小于 6 6 6。
所以可以得到要将数列 4 , 2 , 4 , 5 , 1 4,2,4,5,1 4,2,4,5,1 要分成 3 3 3 段,每段和的最大值最小为 6 6 6。
输入格式
第 1 1 1 行包含两个正整数 N , M N,M N,M。
第 2 2 2 行包含 N N N 个空格隔开的非负整数 A i A_i Ai,含义如题目所述。
数据范围
1 ≤ N ≤ 1 0 5 , M ≤ N , A i ≤ 1 0 8 1≤N≤10^5,M≤N,A_i≤10^8 1≤N≤105,M≤N,Ai≤108
输出格式
一个正整数,即每段和最大值最小为多少。
输入样例
5 3
4 2 4 5 1
输出样例
6
解题思路
没有一套标准的算法用于此题,只能进行搜索。
暴力搜索直接 T T T飞,考虑到题中要求是最小的最大值,所以采用二分搜索法。
二分答案:限制分段和的最大值越小,分段越多;反之,分段越少。
判断函数如下:
bool judge(long long x) {int l = 0, cnt = 0;for (int r = 1; r <= n; r++) {if (arr[r] - arr[l] > x) {l = r - 1;cnt++;}}return ++cnt <= m;//分段数越多,越容易符合题中要求的分段数
}
不断拓展区间,区间和小于上限则继续,大于上限则累计分段数,然后进行下个区间的计算。
二分主体如下:
long long bin_search() {long long l = max_num - 1, r = arr[n] + 1, mid;while (l + 1 != r) {mid = l + r >> 1;if (judge(mid)) r = mid;else l = mid;}return r;//符合题意的最小限制值
}
通过l = nax_num - 1
来限制每段中至少含有一个元素, [ 0 , l ] [0,l] [0,l]是不符合题意的限制值, [ r , a r r [ n ] ] [r,arr[n]] [r,arr[n]]是符合题意的限制值。
最后,AC代码如下:
#include <iostream>
using namespace std;
const int max_n = 1e5;
const int max_a = 1e8;
const int max_m = max_n;long long arr[max_n + 1], max_num = 0;
int n, m;bool judge(long long x) {int l = 0, cnt = 0;for (int r = 1; r <= n; r++) {if (arr[r] - arr[l] > x) {l = r - 1;cnt++;}}return ++cnt <= m;
}long long bin_search() {long long l = max_num - 1, r = arr[n] + 1, mid;while (l + 1 != r) {mid = l + r >> 1;if (judge(mid)) r = mid;else l = mid;}return r;
}int main() {cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> arr[i];max_num = max(max_num, arr[i]);arr[i] += arr[i - 1];//数组前缀和}cout << bin_search() << endl;return 0;
}