农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过2000头。
约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。
围起区域内至少需要包含 F 块地,其中 F 会在输入中给出。
在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。
【输入】
第一行,N和F;
N个正整数,表示A。
【输出】
一个整数,表示答案的1000倍(不用四舍五入,直接输出)。
【输入样例】
10 6
6 4 2 10 3 8 5 9 4 1
【输出样例】
6500
【提示】
n ≤ 100000
sol:二分答案区间的牛可能的最大平均值,用每块地的牛数量减去平均值,求前缀和。再求出满足条件的子区间的部分和,如果部分和>=0,则说明平均值可以调大,否则平均值调小。(为什么要减去平均值后再求前缀和,而不直接计算前缀和后找出最大子串求平均值?因为如果不减的话,我们要找出满足条件的部分区间,这时枚举部分区间的右端点,那么左端点一定是从第一个数起到右端点的前缀和最大,但它不一定是平均值最大,我们还要记录左端点,才求出部分区间平均值最大,但这样要o(n^2)的时间复杂度。如果减掉平均值,前面的数据有正有负,我们就可以像下面程序这样,只枚举右端点就可以找出满足条件的部分最大字串和,时间复杂度为o(n)。)本题总时间复杂度为o(nlogn)
1 //先二分平均数,然后将各个元素减去平均数,处理前缀和。 2 //然后利用前缀和,判断最大子段和是否大于0。 3 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 int n,m;double b[100001],a[100001],sum[100001]; 8 int main() 9 { 10 scanf("%d%d",&n,&m); 11 for(int i=1;i<=n;i++) 12 scanf("%lf",&a[i]); 13 double l=0,r=2000,minn=1e-5; 14 while(r-l>minn) 15 { 16 double mid=(l+r)/2,maxx=1e10,ans=-1e10;//二分平均数 17 for(int i=1;i<=n;i++) 18 b[i]=a[i]-mid,sum[i]=b[i]+sum[i-1];//将每块地的牛的头数都减去平均数,再求前缀和 19 for(int i=m;i<=n;i++)//计算m~n块地的最大字串和 20 { 21 maxx=min(maxx,sum[i-m]);//在sum[0]~sum[i-m]中选最小值 22 ans=max(ans,sum[i]-maxx);//所有sum[i]-maxx中取最大,即最大子段和 23 } 24 if(ans>=0) l=mid;//如果ans>=0,则说明能够到达平均数,平均数还可以再提高 25 else r=mid;//否则说明不能到达,平均数应降低 26 } 27 printf("%d",(int)(r*1000));//题目要求答案*1000 28 }