P1182 数列分段 Section II

题目描述

对于给定的一个长度为N的正整数数列 A_{1\sim N}A1∼N​,现要将其分成 MM(M\leq NM≤N)段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

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

将其如下分段:

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

第一段和为 66,第 22 段和为 99,第 33 段和为 11,和最大值为 99。

将其如下分段:

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

第一段和为 44,第 22 段和为 66,第 33 段和为 66,和最大值为 66。

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

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

输入格式

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

第 22 行包含 NN 个空格隔开的非负整数 A_iAi​,含义如题目所述。

输出格式

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

输入输出样例

输入 #1复制

5 3
4 2 4 5 1

输出 #1复制

6

说明/提示

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

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

对于 100\%100% 的数据,1\leq N\leq 10^51≤N≤105,M\leq NM≤N,A_i < 10^8Ai​<108, 答案不超过 10^9109。

 

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long ll;
const int mas=1e5+5;
ll r;
int v[mas],l;
int N,M;
//最终结果是p,下于p的不成立,大于p的成立,所以二分的区间基本确定
//接下来确定l r; 
//最终结果是res,区间和的最大值最小值是res,如果是res-x,那么结果的分组一定的>n的 
bool check(int m);
int main()
{
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++)
	{
		scanf("%d",&v[i]);
		l=max(l,v[i]);
		r+=v[i];
	}
	while(l<r)
	{
		int mid=l+r>>1;//此时的l 和 r不用加一减一 
		if(check(mid))r=mid;
		else l=mid+1;	
	//	cout<<l<<" "<<r<<endl; 
	}
	cout<<r;
	return 0;
}

bool check(int m)//在check的时候进行准换思路,,在小于m的情况下看需要的的是几个分组
//如果分组大于了对应的N,就不可能(此时的和,或者剩余的空间容不下了此时的数值,就重新开一个) 
{
	int cnt=1,res=m;//剩余的空间,初始的数值1,因为最终的结果是肯定会有一个多出来的 
	for(int i=1;i<=N;i++)
	if(res>=v[i])res-=v[i];
	else res=m-v[i],cnt++;
	
//	cout<<cnt<<endl; 
	return M>=cnt;
	}

总结:

利用二分法作为判断条件进行check(),前提是含有单调性。寻找到单调区间

首先问什么假设什么,假设这个值 是res,凡是大于res的数值(每个区间和都可以大于这个上限)一定可以,小于res的数值(每个区间的和小于res这个上限)一定不可以。

然而res的数值可以的取值范围是l=max(v[i]),r+=v[i]

如何判断每个区间和小于mid的情况下的,区间的最小分块是否大于M,采用贪心的策略,直接可以证明。 

注意:

cnt 的初值是1,因为最终会有一个剩余的一部分没有加入;

在check()中求cnt的写法很美,可以多多的吸收。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值