【洛谷 P1182】数列分段 Section II 题解(二分答案+循环)

数列分段 Section II

题目描述

对于给定的一个长度为N的正整数数列 A 1 ∼ N A_{1\sim N} A1∼N,现要将其分成 M M M( M ≤ N M\leq N 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

样例输入 #1

复制代码
5 3
4 2 4 5 1

样例输出 #1

复制代码
6

提示

对于 20 % 20\% 20% 的数据, N ≤ 10 N\leq 10 N≤10。

对于 40 % 40\% 40% 的数据, N ≤ 1000 N\leq 1000 N≤1000。

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 5 1\leq N\leq 10^5 1≤N≤105, M ≤ N M\leq N M≤N, A i < 1 0 8 A_i < 10^8 Ai<108, 答案不超过 1 0 9 10^9 109。


思路

函数check用来判断当前的x是否满足条件。在函数check中,会遍历数列a[N],将当前位置的数加到sum上,并判断sum是否超过x。如果超过x,则创建新区间,将sum重新赋值为当前位置的数,并将cnt加1。最后,返回cnt是否大于m。

首先读入n和m的值,然后读入数列a[N]的值,并初始化l和r的值。因为所有元素都要划入区间内,所以要保证最大的元素也要被划进某一个区间,l初始化为数列a[N]中的最大值。如果把所有元素都划进区间,那么区间的最大值肯定不会超过所有元素的和,r初始化为数列a[N]中所有数的和。

使用二分法来寻找满足条件的x。在每一次循环中,计算mid的值,并调用check函数判断是否满足条件。如果满足条件,则说明x偏小,将l更新为mid+1。如果不满足条件,则说明x偏大,将r更新为mid-1。

注意:check函数中,需要将区间数cnt初始化为1,因为最少有一个区间。


AC代码

cpp 复制代码
#include <algorithm>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;

const int N = 1e6 + 7;

int n, m;
int a[N];

bool check(int x) {
	int sum = 0;
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		int tmp = sum + a[i];
		if (tmp > x) {
			sum = a[i];
			cnt++;
		} else {
			sum = tmp;
		}
	}
	// cout << x << " " << cnt << endl;
	return cnt >= m;
}

int main() {
	int l, r, mid;
	l = 0;
	r = 0;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		l = max(l, a[i]);
		r += a[i];
	}
	while (l <= r) {
		mid = (l + r) >> 1;
		if (check(mid)) {
			// 偏小
			l = mid + 1;
		} else {
			// 偏大
			r = mid - 1;
		}
	}
	cout << l << endl;
	return 0;
}
相关推荐
不知天地为何吴女士1 小时前
Day32| 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
算法
小坏坏的大世界1 小时前
C++ STL常用容器总结(vector, deque, list, map, set)
c++·算法
liulilittle2 小时前
C++ TAP(基于任务的异步编程模式)
服务器·开发语言·网络·c++·分布式·任务·tap
励志要当大牛的小白菜4 小时前
ART配对软件使用
开发语言·c++·qt·算法
qq_513970444 小时前
力扣 hot100 Day56
算法·leetcode
PAK向日葵5 小时前
【算法导论】如何攻克一道Hard难度的LeetCode题?以「寻找两个正序数组的中位数」为例
c++·算法·面试
爱装代码的小瓶子6 小时前
数据结构之队列(C语言)
c语言·开发语言·数据结构
爱喝矿泉水的猛男7 小时前
非定长滑动窗口(持续更新)
算法·leetcode·职场和发展
YuTaoShao7 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
YouQian7727 小时前
Traffic Lights set的使用
算法