题目链接:
https://www.luogu.com.cn/problem/P1182https://www.luogu.com.cn/problem/P1182
题目:
对于给定的一个长度为 N 的正整数数列 ,现要将其分成 M(M≤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 个空格隔开的非负整数
,含义如题目所述。
输出格式:
一个正整数,即每段和最大值最小为多少。
例:
输入:
5 3 4 2 4 5 1
输出:
6
数据提示:
对于 100% 的数据,1 ≤ N ≤ ,M ≤ N,
<
, 答案不超过
。
思路:
首相,看题目是要求每段和最大值最小为多少。当我们看到最大或最小时,我们应该要迅速的反应过来这是一道二分算法题。那么我们可以拿出我们的二分算法模板。
void Hlaf() {
int l, r;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (条件\函数) {
l = mid;
}
else {
r = mid;
}
}
}
我们只需要对二分算法模板进行稍微改动即可。
由于结果数字过大,所以我们都采用long long类型来存储。(宏#define ll long long可以让long long简写为ll)。
题目要求每段和最大值最小为多少,那最大值可能是所以数字之和,所以我们定义一个整形r,在每一次接收数字时,加上该数字,求得所有数字之和,同时r也是该二分的右界;最大值也可能是所有数字中的最大值,所以我们定义一个l,在每一次接收数据时,比较数字的大小,取最大值,同时l也是二分算法的左界。
确定完二分的上下界,就应该确定二分算法的精髓——判断条件了。因为题目要求将数字分成连续的m段,所以可以计算若当前的二分中点mid为最大值时,是否能够将这些数字分成连续的m段。所以我们使用一个布尔函数Check,将mid传入该函数。在函数中,我们定义一个times用于表示分了几组,ans表示该组所有数字之和。接着使用for循环遍历所有数字,当ans加上当前数字小于等于mid时,我们继续循环;否则,我们让times加一,并且让ans等于0,然后再加上当前数字。最后我们返回times与m大小的比较结果了。
当Check函数返回为真(true)时,我们让l等于mid+1;否则,让r等于mid-1。
AC代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
ll n, m, arr[100005] = { 0 }, res;
bool Check(ll mid) {
ll times = 0, ans = 0;
for (int i = 1; i <= n; i++) {
if (ans + arr[i] > mid) {
times++;
ans = 0;
}
ans += arr[i];
}
return times >= m;
}
int main() {
scanf("%lld%lld", &n, &m);
ll l = -1, r = 0;
for (ll i = 1; i <= n; i++) {
scanf("%lld", &arr[i]);
l = max(l, arr[i]);
r += arr[i];
}
while (l <= r) {
ll mid = l + r >> 1;
if (Check(mid)) {
l = mid + 1;
}
else {
r = mid - 1;
}
}
printf("%lld", l);
return 0;
}
如果我的文章对你有所帮助,不妨给我个关注何点赞吧