題意
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;
}