首页 > 其他 > 详细

P1404 平均数

时间:2020-03-27 10:09:02      阅读:63      评论:0      收藏:0      [点我收藏+]

描述:
\(给一个长度为 n 的数列,我们需要找出该数列的一个子串,使得子串平均数最大化,并且子串长度 ≥m。\)

题目传送门:平均数

题目算法:二分+DP+思维

分割线

又是束手无策的一道题目,理解起来也很反人类.......以下见解摘自这位dalao

Ⅰ.如何进行二分

二分最大平均值,然后进行验证。如何验证?将序列中的元素减去平均值

如果有一段长度不小于L的区间和大于等于0,说明该平均值可行,调整边界,继续二分。

Ⅱ.如何求有限制性最大子段和

方法1

求一个子段,它的和最大,没有“长度不小于L”这个限制。这个可以用动态规划解决。 \(d[i]=max(d[i-1]+A[i],0.0)\);

\(d[i]\)为以i结尾的最大子段和

如何将“限制性”转化为一般情况:

\(s[i]\)为以i结尾且长度不小于L的最大子段和,\(A[i]\)为原数组,\(sum[i]\)为前缀和。

因为长度要不小于L,所以从\(A[i-L]\)一直到\(A[i]\)是必选的,总和为\(sum[i]-sum[i-L]\),前面\(i-L\)个数可选可不选

要想和最大,就要加上以\(a[i-L]\)结尾的最大子段和,所以\(s[i]=sum[i]-sum[i-L]+d[i-L]\) (前面\(i-L\)个数的最大子段和)

最后在\(s[i]\)中取一个最大值就是\(ans\)。也可以不用s数组,直接在过程中取最大值

方法2

最大子段和可以转化为前缀和相减的形式。

设sum[i]为序列A的前i项的和 ,设s[i]是序列A以A[i]结尾且长度不小于L的最大连续子段和

那么显然有: \(s[i] = sum[i] - min\){\(sum[j]\)}\((0<=j<=i-L)\)

这里的代码用的是第一种方法。

#include <bits/stdc++.h>
using namespace std;
const int maxn=100009;
const double eps=1e-7;
int n,m;
double a[maxn],b[maxn],dp[maxn],sumn[maxn];
bool check(double mid)
{
	for(int i=1;i<=n;i++)
	{
		b[i]=a[i]-mid;
		sumn[i]=sumn[i-1]+b[i];
		dp[i]=max(dp[i-1]+b[i],0.0);
	}
	for(int i=m;i<=n;i++)
		if(dp[i-m]+sumn[i]-sumn[i-m]>=0)	return true;
	return false;
}
int main()
{
	scanf("%d%d",&n,&m);
	double l=0,r=0,mid;
	for(int i=1;i<=n;i++)
	{
		scanf("%lf",&a[i]);
		r=max(r,a[i]);
	}
	while(r-l>eps)
	{
		mid=(l+r)/2.0;
		if(check(mid))	l=mid;
		else	r=mid;
	}
	printf("%d",int(r*1000));
}

P1404 平均数

原文:https://www.cnblogs.com/iss-ue/p/12579227.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!