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.
Input
* Line 1: Two space-separated integers, N and F.
* Lines 2..N+1: Each line contains a single integer, the number of cows in a field. Line 2 gives the number of cows in field 1,line 3 gives the number in field 2, and so on.
Output
* Line 1: A single integer that is 1000 times the maximal average.Do not perform rounding, just print the integer that is 1000*ncows/nfields.
Sample Input
10 6 6 4 2 10 3 8 5 9 4 1
Sample Output
6500
题意: 给出n个数,要求寻找一段长度不小于k的区间,并使其平均值最大,将最大平均值*1000再取整输出。
分析: 比较容易想到要二分答案,那么check(x)函数需要判断是否存在一段长度不小于k的区间且其平均值大于等于x,而且要在O(n)的复杂度以内。由于答案是输出整数,这里我们用整数上的二分就可以了,因为a[i]都在[1, 2000]范围内,平均值*1000应该在[1000, 2000000]范围内,所以要在1000到2000000内二分,但如果你这么写一定会wa一天(我就是),因为这题题目有问题,存在a[i]为0的数据!也就是要在0到2000000范围内二分。避开这个坑点后考虑如何设计check函数,我们假设区间左端点为l,右端点为r,check中就需要我们判断是否存在[l, r]使得(s[r] - s[l-1]) / (r-l+1) >= x,变个形也就是s[r] - s[l-1] - x*(r-l+1) >= 0,左边这个值其实就是[l, r]内每个值都减去x然后再求和,也就是对原数组a[i]处理一下得到一个新的t数组,t[i] = a[i] - x,这样有什么好处呢?此时我们只需要判断是否存在一段长度不小于k的区间且其和 >= 0,这个问题其实就是求一下连续区间和的最大值,若最大值 >= 0那么return true,反之return false。至于连续区间和的最大值就用前缀和求就行了,最暴力的做法是外层for循环从[k, n]枚举区间右端点r,然后内层for循环从[1, r-k+1]枚举左端点l,不过这样显然会超时,其实内层for循环是可以被优化掉的,如果我们知道前缀和数组s[0]~s[i]的最小值(很容易预处理出来),那么直接s[r]减去s[0]~s[r-k]的最小值就是以r为右端点的长度不小于k的所有区间和的最大值,这个过程是O(1)的,因此check的总时间复杂度为O(n)。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
using namespace std;
int n, k, a[100005], t[100005], s[100005];
bool check(int x)//判断是否存在一段长度大于等于k的且平均值大于等于x的区间
{
for(int i = 1; i <= n; i++)
{
t[i] = a[i]*1000-x;
s[i] = s[i-1]+t[i];
}
int ans = -0x3f3f3f3f3f3f3f3f;//ans保存长度大于等于k的区间和的最大值
int _min = 0;//_min保存s[0]~s[i-k]的最小值
for(int i = k; i <= n; i++)//枚举区间右端点,左端点取[1,i-k+1]
{
_min = min(_min, s[i-k]);
ans = max(ans, s[i]-_min);
if(ans >= 0)
return true;
}
return false;
}
signed main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
int l = 0, r = 2000000, ans = -1;
while(l <= r)
{
int m = l+r>>1;
if(check(m))
{
ans = m;
l = m+1;
}
else
r = m-1;
}
cout << ans;
return 0;
}