黑客的平均数

离开了老久,终于又回机房了,有点小激动,发誓要好好学习了,相信在将近四个月的平静后我不会再像之前那么水了,花了一个小时写了这道题,而且是看题解的,没事慢慢来不着急。

题面:

   给出长度为n的序列,求长度不小于k的连续子序列,且这个子序列的平均值为最大。

   输出这个最大平均值(保留六位小数)

题解:

  1)低配版 (n,k<=5000)  枚举左右端点找最大值

  2)高配版(n,k<=1e5, a[ i ] < 5000)   二分  

    对于每个二分值mid进行check,如果存在更优解,则:

            

    那我们把每个a[i]减去一个mid,所以检查时建立个aa[i]=a[i]-mid,b[i]=b[i-1]+aa[i],

    问题就转化为:是否存在一个长度满足要求的区间和不等于0。

    可以建立一个f数组实现,f[i]表示以i结尾的连续子序列的最大平均值,从左往右扫一遍的过程中更新 f[i] 和 ans,如果ans大于0,则存在更优解。

    

#include <bits/stdc++.h>
using namespace std;
inline double read()
{
    char ch=getchar();
    int s=0,f=0;
    while(!(ch<='9'&&ch>='0')) {f|=ch=='-';ch=getchar();}
    while(ch<='9'&&ch>='0') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return f?-s:s;
}
int n, k;
double l=1e9, r=-1e9, ans, mid;
double f[10004],aa[10004],b[10004],a[10004];
bool check(double x)
{
    ans = -1e9; 
    for(int i=1;i<=n;i++)
        aa[i] = a[i]-x, b[i] = b[i-1]+aa[i];
    
    memset(f,-10,sizeof(f));
    for(int i=k;i<=n;i++)//枚举右端点,f[i]表示以i结尾的连续子序列的最大平均值
        f[i] = max(f[i-1]+aa[i],b[i]-b[i-k]),ans = max(ans,f[i]);
        
    if(ans>=0) return true;
    else return false;
    
} 
int main()
{
    n = read(); k = read();
    for(int i=1;i<=n;i++)
        a[i] = read(), l = min(l,a[i]), r = max(r,a[i]);
    //找到答案的范围l,r
    while(fabs(r-l)>1e-10)
    {
        mid = (r+l)/2;
        if(check(mid)) l = mid;
        else r = mid; 
    }
    printf("%.6lf\n",l);
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lyflalala/p/11323330.html