最佳牛围栏

题目链接

题意

把一个长度为N的序列取出一段长度不小于F的子段,求平均值最大的子段的平均值。

思路

答案似乎不容易看出单调性。。。

换个思路, 一段序列减去平均值后得到的序列, 序列和一定为0.

如果减去的值小于平均值, 则序列和为正, 反之为负。

可以二分一个值, 然后减去这个值, 如果序列中长度不小于F的最大子段和为正, 说明平均值可以再大一些。。

单调性就可以看出来了: 二分值越大, 序列所有数减去二分值后得到的最大子段段和就越小。

关键便是快速判断最大子段和。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<set>
#include<cmath>
using namespace std;

const int mx = 1e5 + 5;
const int inf = 1e9;

inline void fre(){ freopen("1.txt", "w", stdout); }

int n, L;
double a[mx], sum[mx];

inline bool check(double mid){
    sum[0] = 0;
    double x;
    for(int i = 1; i <= n; i++) x = a[i] - mid, sum[i] = sum[i-1] + x;
    double minn = inf, res = -inf;
    for(int i = L; i <= n; i++){
        minn = min(minn, sum[i-L]);
        res = max(res, sum[i] - minn);
    } 
    if(res >= 0) return 1;
    return 0;
}

int main(){
    //fre();
    cin >> n >> L;
    double l = inf, r = 0;
    for(int i = 1; i <= n; i++) scanf("%lf", &a[i]), l = min(l, a[i]), r = max(r, a[i]);
    
    while(r - l > 0.000001){
        double mid = (l+r)/2;
        if(check(mid)) { l = mid;}
        else r = mid;
    }
    
    printf("%d\n", (int)(r*1000));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Maktub-blog/p/10995044.html