首先简化问题,给一段长度为n的序列,求长度大等于L的子段,他的平均值最大。
考虑二分答案(因为平均值具有单调性),将求解转化为判定。
当我们二分了一个平均值,让原序列都减去这个平均值,判定是否存在长度大等于L的子序列和非负,即求最大子段和非负。
经典的最大子段和问题,加上长度限制,列出表达式:
sumi表示前缀和,观察表达式,随i增加1,j也增加1,故不需枚举j,只需用一个变量记录min{sumj}
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-6;
int n, L;
double a[100001], b[100001], sum[100001], zx = 2001, zd = 0;
bool check(double x) {
double ans = -0x3f3f3f3f;
memset(sum, 0, sizeof(sum));
memcpy(b, a, sizeof(b));
for (int i = 1; i <= n; i ++) {
b[i] -= x;
sum[i] = sum[i - 1] + b[i];
}
double sumj = 0x3f3f3f3f;
for (int i = L; i <= n; i ++) {
sumj = min(sumj, sum[i - L]);//记录min{sumj}
ans = max(ans, sum[i] - sumj);
}
if (ans >= 0) return 1;
else return 0;
}
int main() {
cin >> n >> L;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
zx = min(zx, a[i]);
zd = max(zd, a[i]);
}
double l = zx, r = zd;//平均值的上下界
while (l + eps < r) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
//cout << (int) r * 1000;错误
cout << int (r * 1000);
}