题意

Farmer John's farm consists of a long row of N (1 <= N <= 100,000)fields. Each field contains a certain number of cows, 1 <= ncows <= 2000.

FJ wants to build a fence around a contiguous group of these fields in order to maximize the average number of cows per field within that block. The block must contain at least F (1 <= F <= N) fields, where F given as input.

Calculate the fence placement that maximizes the average, given the constraint.

大意是说,给你一个正整数序列,找出一个区间使得平均值最大,要求该区间的长度大于等于F。

方法

做法来源于先辈,再在此特别感谢
据说有类似凸包优化,学会后再写一遍吧

注意到(1 <= N <= 100,000),因此传统方法(暴力)就算上了前缀和,时间复杂度也是平方级别的,必然超时。

方法是:先二分答案。接着对于每个候选答案,尽量在O(n)时间内验证。

那么怎么尽快验证答案呢?

想象一个情景:一个固定左端起点长度为F的区间向右生长,直至平均值对大。循环n次来更换不同起点,所有情况都能考虑周全。

这里需要优化。首先,知道平均值要知道总和,长度为F时的和可以用前缀和一步得到。

向右延长这步也可以类似去做。我们要首先把所有整数都减去当前的候选答案。这一步很关键,因为我们要获取的是 平均值的最大,减去后,我们便可以直接使用确定起点向右的数字的和作为状态,而不用再考虑除掉个数来计算对平均值的影响。

定义t[i]为以i为起点,向右延长的最大和。如果t[i+1]>=0,那么t[i]=t[i+1]+w[i]。若t[i+1]<0,那么不如扔掉i+1及其之后的数字,故此时t[i]=w[i]。

这样,能保证O(n)时间内验证候选答案。

代码

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int MAXN=100000+5;
int N,F;
double num[MAXN];
double sum[MAXN];
double rmaxsum[MAXN];
double l=9999999,r;
inline bool qualify(double tans){
    for(int i=N;i>=1;i--)
        rmaxsum[i]=max(num[i]-tans,rmaxsum[i+1]+num[i]-tans);
    for(int i=1;i<=N-F+1;i++){
    //  cout<<sum[i+F-1]-sum[i-1]<<" "<<F*tans<<" "<<tans<<endl;
        if(sum[i+F-1]-sum[i-1]>=F*tans)
            return true;
    //  cout<<"sec:"<<sum[i+F-1]-sum[i-1]-F*tans+rmaxsum[i+F]<<endl;
        if(sum[i+F-1]-sum[i-1]-F*tans+rmaxsum[i+F]>=0)
            return true;
    }
    return false;
}
int main(){
    cin>>N>>F;
    for(int i=1;i<=N;i++){
        scanf("%lf",&num[i]);
        sum[i]=sum[i-1]+num[i]; 
        r=max(r,num[i]);
        l=min(l,num[i]);
    }
    while(l<r-0.0001){
        double mid=(r+l)/2;
        if(qualify(mid))
            l=mid;
        else
            r=mid;
    }
    cout<<int(r*1000)<<endl;
    return 0;
} 

 posted on 2017-01-29 18:56  cylcy  阅读(2948)  评论(0编辑  收藏  举报