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

前往:我自己搭建的博客

题目

洛谷P1182数列分段Section II

题解

此题要用到二分法,但不是传统意义上的在单调序列中查找数值,而是二分答案转化为判定,比较抽象。首先,定义“如果能将数列分为m段,且每段长度小于等于sum,则称sum为合法值”,那么题中要求的就是最小的合法值。易证:越大的sum越容易合法,且存在分界值s,小于s的sum都不合法,大于等于s的sum都合法(s就是题目要求的值)。将合法值简称“1”,不合法值简称“0”。则对于从小到大的sum有如下序列:……0000011111……。若从小到大或从大到小一个个验证合法性,速度较慢,考虑二分。二分的左右指针为l和r,mid=(l+r)/2,它们都表示sum值。如果mid为合法值,则s在其左侧或是其本身,那么搜索范围可由[l,r]缩小为[l,mid];如果mid为非法值,则s在其右侧,范围由[l,r]缩小为[mid+1,r]。

下面讲如何进行合法性检测:贪心即可。从左往右分段,每一段尽量多,如果最后分的段数小于等于m,则合法。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int m,n;
ll a[maxn];
inline bool check(ll sum)	//判断每段长度<=sum是否可行 
{
	ll now=0,cut=1;	//now表示当前段已有长度,cut表示当前是第几段(注意初始值是1,不是0) 
	for(int i=1;i<=n;i++)
	{
		if(a[i]>sum) return 0;	//注意这种情况 
		if(now+a[i]>sum)
		{
			if(cut==m) return 0;
			now=a[i];
			cut++;
		}
		else now+=a[i];
	}
	return 1;
}
inline ll erfen()
{
	ll l=0,r=0;
	for(int i=1;i<=n;i++) r+=a[i];
	while(l<r)
	{
		ll mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1; 
	}
	return l;
}
		
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	printf("%lld\n",erfen());
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zjgmartin/article/details/108415813