【二分】数列分段 Section II

L i n k Link

l u o g u luogu P 1182 P1182

D e s c r i p t i o n Description

对于给定的一个长度为N的正整数数列 A i A−i ,现要将其分成 M ( M N ) M(M≤N) 段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列 42451 4 2 4 5 1 要分成 3 3

将其如下分段:

[ 4 [4 2 ] [ 4 2][4 5 ] [ 1 ] 5][1]

第一段和为 6 6 ,第 2 2 段和为 9 9 ,第 3 3 段和为 1 1 ,和最大值为 9 9

将其如下分段:

[ 4 ] [ 2 [4][2 4 ] [ 5 4][5 1 ] 1]

第一段和为 4 4 ,第 2 2 段和为 6 6 ,第 3 3 段和为 6 6 ,和最大值为 6 6

并且无论如何分段,最大值不会小于 6 6

所以可以得到要将数列 42451 4 2 4 5 1 要分成 3 3 段,每段和的最大值最小为 6 6

I n p u t Input

第1行包含两个正整数 N M N,M

第2行包含 N N 个空格隔开的非负整数 A i A_i ,含义如题目所述。

O u t p u t Output

一个正整数,即每段和最大值最小为多少。

S a m p l e Sample I n p u t Input

5 3
4 2 4 5 1

S a m p l e Sample O u t p u t Output

6

H i n t Hint

对于 20 20% 的数据,有 N 10 N≤10

对于 40 40% 的数据,有 N 1000 N≤1000

对于 100 100% 的数据,有 N 100000 , M N , A i 1 0 9 N≤100000,M≤N, A_i之和不超过10^9

T r a i n Train o f of T h o u g h t Thought

这题我用二分来做
因为要涉及到区间和,所以,前缀和优化,然后二分答案
具体看代码

C o d e Code

#include<iostream>
#include<cstdio>

int n, m, sum[100005];

using namespace std;

bool check(int mid)
{
	int end = 1, start = 0, cnt = m;
	while (cnt--)
	{
		while (sum[end] - sum[start] <= mid && end <= n) 
			end++;//枚举当前分段的右端点
		start = end - 1;//记录下一段的左端点
		if (start == n) return 1;
	}
}

int main()
{
	int ans = 0;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
	{
		int x;
		scanf("%d", &x);
		sum[i] = x + sum[i - 1];//前缀和
 	}
 	int left = sum[1], right = sum[n];
 	while (left <= right)
 	{
 		int mid = (left + right) >> 1;
 		if (check(mid)) ans = mid, right = mid - 1;//二分的答案其实就是题目的答案
		  else left = mid + 1;	
 	}
 	printf("%d", ans);
 	return 0;
} 

猜你喜欢

转载自blog.csdn.net/LTH060226/article/details/102841480