题意大意就是:给定一个数列,问这个数列的长度大于F的子段的平均值最大是多少
如果直接遍历找到最大平均数的区间需要遍历两次,O(n^2),
但用有一种O(n)的办法可以判定存不存在一个区间,它的平均数超过某个数。
因此可以用二分搜索,在[0, 2000]的范围内搜索这个最大平均数。
而且题目也没有题目要求我们要去找到这个序列是什么,所以我们只需要判断是否有一个区间使得上述命题成立就行了,从而转换一个判定的问题
对于区间平均数,我们可以不需要每次都讲平均值计算一遍,我们可以把每个数减去他们所有的平均值,如果大于0就代表它比平均数大,这样使用一个前缀和我们就可以判断某个区间内的平均数是不是比原来的平均数大了
题目中要求的序列长度是要求比F要大的,我们可以采用双指针的算法;我们要看的是这个区间内的平均值能不能比当前的这个平均值要大,就是求这个操作(减平均数)之后的序列的某一段的和是不是要大于等于0
而我们看看每个大于F的区间有上述的命题成立,那么我们只需要让被减数尽可能的小就可以了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int n;
int f;
int a[100000 + 10];
double s[100000 + 10];
bool check(double av){
for(int i = 1; i <= n; i++){
s[i] = s[i - 1] + a[i] - av;
}
double minn = 20000;
for(int i = 0, j = f; j <= n; j++, i++){
minn = min(minn, s[i]);
if(s[j] - minn >= 0){
return 1;
}
}
return 0;
}
int main(){
cin >> n >> f;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
double l = 1;
double r = 2000;
while(r - l > 1e-6){
double mid = (l + r) / 2;
if(check(mid)){ // 存在一个区间,使得他们的平均值大于等于mid
l = mid;
}
else{
r = mid;
}
}
cout << int(r * 1000); // 如果答案是6500,那么l的取值虽然可以和r无限接近,但永远小于6500,所以取整之后就会得到6499,就错了。
re 0;
}