题意 :给定正整数数列A,求一个平均数最大的,长度不小于L的(连续的)子段。
分析 :如果没有 “平均数” 和 “长度不小于L” 这两个限制,那么这就是一个最大子段和的问题。我们可以用二分的方法找到一个数,假设这个数是最大的平均数,将数列A中所有位置减去这个数,那么这个题就变成将数列A减去一个数,求大于0的字段和(长度不小于L)。解决了“平均数”问题再来看“子段不小于L”这个限制,我们可以用前缀和的方法在线的求出任意一段子段和,用sum表示数列A的前缀和,所以我们从第L个开始(可以直接从长度为L开始,可以免去长度小于L
的情况)for循环每次增加一个长度,记录最小sum(假设现在循环到了L+2个,那么sum[1]和sum[2]中找出那个最小的数,再用sum[L+2]-min得到的就是最大子段和)也是增加一个。
大致代码
*注意:用cin会tle 坑了我好久QAQ
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5+5;
double a[N],sum[N],temp[N];
int main()
{
double eps = 1e-5,ans=0;
int n,L;
double l=1e8,r=-1;
memset(a,0,sizeof a);
memset(sum,0,sizeof sum);
memset(temp,0,sizeof temp);
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++){
scanf("%lf",&a[i]);
l=min(l,a[i]);
r=max(r,a[i]);
}
while(l+eps<r){
double mid = (l+r)/2;
for(int i=1;i<=n;i++) temp[i] = a[i]-mid;
for(int i=1;i<=n;i++) sum[i] = temp[i]+sum[i-1];
double minl=1e8,maxl=-1;
int size=0;
for(int i=L;i<=n;i++){
minl=min(minl,sum[size++]);
maxl=max(maxl,sum[i]-minl);
}
if(maxl>=0) l=mid;
else r=mid;
}
cout<<int(r*1000);
return 0;
}