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;
}
原文地址:https://www.cnblogs.com/hnkjdx-ssf/p/14201693.html