AcWing - 最佳牛围栏(二分、前缀和)

题目链接
  题目要求我们求出满足条件的最大值,因为\(f\)的长度不是固定的,所以如果直接求解的话,非常麻烦。但是,如果我们用二分判定答案的话,问题就变得简单了。但是二分必须要区间具有单调性,我们怎么让区间具有单调性呢?对于一个平均值,如果它偏大的话,那么这个区间里面任意长度的序列的平均值都取不到这个值,换句话说,如果让他们的平均值与二分的平均值比较,结果肯定是负的,同理,对于一个偏小的平均值,一定可以在序列里面找到一个连续子序列,它的平均值大于二分的平均值。
  但是这时候又会有一个问题出现了,我们怎么在最短的时间内找到一个序列判断它的平均值是否大于\小于我们二分的平均值呢?这里,我们不需要把所有的可能全都枚举出来。我们先让整个序列全都减去我们二分的平均值(实现起来并不需要这么麻烦,这里只是方便理解),然后我们所要做的就是找一个连续子区间,它的区间和大于\(0\)。这个问题还能进一步转化为我们找一个从\(1\)开始以\(k\)结尾的区间(区间长度大于\(f\),而且\(k\leq n\)),然后判断在这个区间内有没有一个以\(l\)结尾的区间(\(l\leq k-l\)),这样的话,我们只要对这个区间做一个前缀和来处理一下就行了

//https://www.cnblogs.com/shuitiangong/
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<climits>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define endl '\n'
#define rtl rt<<1
#define rtr rt<<1|1
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define zero(a) memset(a, 0, sizeof(a))
#define INF(a) memset(a, 0x3f, sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define _test printf("==================================================\n")
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
typedef pair<ll, ll> P2;
const double pi = acos(-1.0);
const double eps = 1e-7;
const ll MOD =  1000000009;
const int INF = 0x3f3f3f3f;
template<typename T> void read(T &x){
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
const int maxn = 1e5+10;
double cow[maxn];
int n, f;
bool checker(double mid) {
    double minn = 0;
    for (int i = f; i<=n; ++i) {
        if (cow[i-f]-mid*(i-f) < minn) minn = cow[i-f]-mid*(i-f);
        if (cow[i]-mid*i>minn || fabs(cow[i]-mid*i-minn)<eps) return true;
    }
    return false;
}
int bysh() {
    double l = 1.0, r = 2000.0;
    while(l+eps<r) {
        double mid = (l+r)/2.0;
        if (checker(mid)) l = mid;
        else r = mid;
    }
    return int(r*1000);
    //return int((l+eps)*1000); 上面和这个都行,但是下面这个不行 
    //return int(l*1000); 因为精度问题,l可能小于我们取到的值
}
int main(void) {
    while(~scanf("%d%d", &n, &f)) {
        for (int i = 1; i<=n; ++i) {
            scanf("%lf", &cow[i]);
            cow[i] += cow[i-1];
        }
        printf("%d\n", bysh());
    }
    return 0;
}
posted @ 2020-03-23 10:42  shuitiangong  阅读(235)  评论(0编辑  收藏  举报