洛谷P1182 数列分段 Section II(二分+贪心)

题目描述

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

关于最大值最小:

例如一数列 4   2   4   5   1 4\ 2\ 4\ 5\ 1 4 2 4 5 1 要分成 3 段。

将其如下分段:

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

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

将其如下分段:

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

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

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

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

输入格式

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

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

输出格式

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

输入输出样例

输入

5 3
4 2 4 5 1

输出

6

说明

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

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

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

思路分析(二分+贪心)

  1. 简化题目为:
    将 n 个数分成相邻的 m 组,每组元素都是连续的(同时也不能为空)。求在所有的分法中,使得每组和的 最大值最小的值是多少?

  2. 我们先想一个简单的问题:假定每组数和的最大值不超过v ,是否存在把数组分 为不多于m 段的方法? 不超过v 的前提下,每组数尽量多就行。

  3. 转换思路:每组数和最大值不超过v,求解至少分多少组。将这个值与 m 比较。 如果组数小于m组,则符合条件。假如蓝色是满足的v值,红色是不满足的,那交界处(v的最小值)就是我们要求的结果(每段和最大值最小)。
    在这里插入图片描述

  4. 那么交界处的v值怎么求呢?二分

AC的C++代码

#include<iostream>
#include<algorithm>
using namespace std;

int a[100005],n,m,le,ri,mid;

//函数check判断v值是否满足条件
//返回值:若v满足条件,返回 1
bool check(int v)
{
	int num = 0,sum = 0;
	for(int i = 0;i < n;i++)
	{
		if(sum+a[i] <= v)	// 组内所有数的和没有超过v
			sum += a[i];
		else	//若加上a[i]超过v,所以从a[i]开始重新算作新的一组 
			sum=a[i],num++;	
	}
	return num < m;
}

int main()
{
	cin>>n>>m;
	for(int i = 0;i < n;i++)
	{
		cin>>a[i];
		le = max(le,a[i]);	//每段和最大值最小(分为n组) 
		ri += a[i];			//每段和最大值最大(分为1组) 
	}
	while(le <= ri)
	{
		mid = (le + ri)/2;
		if(check(mid))	
			ri = mid-1; //v=mid时,组数比m小,那么我们要使组数变大,那么v值就要变小,继续在[l,mid-1]寻找
		else
			le = mid+1;
	}
	cout<<le;
    return 0;
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值