题目链接:POJ2018
题目大意: 给出
n
(
n
≤
100000
)
n(n\leq100000)
n(n≤100000)个数字,求出一个长度不小于L的子段,使其平均值最大。
题目分析: 对于一整个数字序列,考虑如何求出最大的平均值,我们考虑假设平均值为
v
v
v,那么如果将数列
a
i
a_i
ai的值全部剪掉
v
v
v获得新的数列
b
i
b_i
bi,
b
i
=
a
i
−
v
b_i=a_i-v
bi=ai−v,那么问题就转化为了求
b
i
b_i
bi中最长的子段(子段长度必须大于等于L),使其和最大。
我们设
s
u
m
i
=
∑
k
=
1
i
b
k
sum_i=\sum^{i}_{k=1}{b_k}
sumi=k=1∑ibk那么要求出最大的
s
u
m
i
sum_i
sumi只需求出
m
i
n
{
s
u
m
j
}
∣
j
≤
i
−
L
min\left\{ sum_j \right\}|_{j\leq i-L}
min{sumj}∣j≤i−L最后在通过二分来找到最大的平均值。代码如下:
题目代码
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define eps 0.000001
#define max(a,b)(a>b?a:b)
int n,m;
double arr[110000],brr[110000],sum[110000];
bool Check(double x){
double minn=200000000.0,ns=-200000000.0;
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
brr[i]=arr[i]-x;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+brr[i];
for(int i=m;i<=n;i++){
if(minn>sum[i-m])minn=sum[i-m];
ns=max(ns,sum[i]-minn);
}
if(ns>=0)return true;
else return false;
}
double Binary(){
double L=0,R=10000000,ans;
while(R-L>eps){
double mid=(L+R)/2;
if(Check(mid)){
ans=mid;
L=mid;
}
else R=mid;
}
return ans+eps;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lf",&arr[i]);
double ans=Binary()*1000;
printf("%d\n",(int)ans);
return 0;
}