题目链接: AcWing 102. 最佳牛围栏
问题描述
分析
这道题是一道比较巧妙的二分+前缀和问题,首先数据有1e5
暴力枚举肯定行不通。
这里考虑二分方法,二分可能的avg
,如果check(avg)=true
这表示能够得到当前的avg
,重点是如何判断当前的avg
是可行的,二分的时间复杂度为O(logn)
,所以check
的复杂度应该为O(n)
的才行。
check
函数只需要判断当前avg
是否能取到就行,这里有一个巧妙地方法,我们让每个数减去avg
,那么前缀和(>=f个数)的值大于0即可,如果不让每个数减去avg
,那么我们需要算
h
[
i
]
−
h
[
j
]
i
−
j
>
=
a
v
g
\frac{h[i]-h[j]}{i-j}>=avg
i−jh[i]−h[j]>=avg,如果减去avg
,我们只需要
h
[
i
]
−
h
[
j
]
>
=
0
h[i]-h[j]>=0
h[i]−h[j]>=0就能说明当前avg
可行。
考虑以某一个位置(>=f)为结尾位置的最大avg
,也就是固定h[i]
,所以只需要求最小的h[j],j>=0,j<=i-f
即可,因为最小的h[j]
如果使得
h
[
i
]
−
h
[
j
]
>
=
0
h[i]-h[j]>=0
h[i]−h[j]>=0就能说明当前avg
可行,否则avg
在以i
位置为结尾不可行。因为i
从小到达遍历,所以可以记录下所有j>=0,j<=i-f
的最小值。
这道题还是挺精妙的,分析没看懂可以看代码,应该比较容易理解。
代码如下
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
double f[N];
double h[N];
int n,k;
bool check(double avg){
for(int i=1;i<=n;i++) h[i]=h[i-1]+f[i]-avg;
double mins=0;
for(int i=k;i<=n;i++){
mins=min(mins,h[i-k]);
if(h[i]-mins>=0) return true;
}
return false;
}
int main(){
double l=0,r=0;
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%lf",&f[i]);
r=max(r,f[i]);
}
while(r-l>=1e-5){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%d\n",int(r*1000));
return 0;
}