102.最佳牛围栏
原题链接:102. 最佳牛围栏
解题思路
本题题意为:给定正整数序列A,求一个平均数最大的、长度不小于L的(连续的)子段。
首先我们可以这样理解,我们要寻找一段数列,这个数列满足,长度不小于L,并且它的子段和非负。也就是我们需要的二分判定。
二分:首先我们的mid=(l+r)/2
,记住这里不要是>>1,因为这是浮点数除法。然后呢,我们可以进行前缀和运算,s[i]=s[i−1]+a[i]−mid
,因为首先我们找的mid是这个平均值,其次前缀和,是为了处理子段和,比如说我要算出[3,5]的子段和,那么我们只需要输出s[5]−s[2]即可。
这里呢,我们要找到这个满足题意的最优解[l,r],那么也就是说a[l−1]要尽量地小,然后a[r]要尽量地大,所以说我们就需要枚举这个l,但是这样的话时间吃不消,那么怎么办呢?我们发现,每一次r变大后,l的取值范围从[1,l]变成了[1,l+1],所以说我们只需要一个变量,存储当前的最小值即可。具体可看代码实现。
样例代码
#include<bits/stdc++.h>
using namespace std;
double a[100001],b[100001],sum[100001];
int main()
{
int N,L;
cin>>N>>L;
for(int i=1;i<=N;i++)
cin>>a[i];
double eps=1e-5;
double l=-1e6,r=1e6;
while(r-l>eps)
{
double mid=(l+r)/2;
for(int i=1;i<=N;i++)
b[i]=a[i]-mid;
for(int i=1;i<=N;i++)
sum[i]=(sum[i-1]+b[i]);
double ans=-1e10;
double min_val=1e10;
for(int i=L;i<=N;i++)
{
min_val=min(min_val,sum[i-L]);
ans=max(ans,sum[i]-min_val);
}
if(ans>=0)
l=mid;
else
r=mid;
}
cout<<int(r*1000)<<endl;
return 0;
}