题目链接:
http://39.98.219.132/problem/1892
题目大意:
长为 n 的面包,每一片面包都有一个美味值,某一段面包的平均美味值是 这段面包的美味值之和 / 这段面包的长度 ,在其中选择长为 [s, t] 的一段面包,找出最大的一段的平均值 。
思路:
首先考虑 暴力,通过 前缀和 求出每个区间的平均值,时间复杂度 O(n * n),你可以拿到 40分 。
优化,最后的取值只可能在 -10000 到 10000 之间,于是我们考虑 二分答案 去猜最大平均值,接下来就只用 验证 猜测的值就是最大值就可以了。
我们将每个数字都减去平均值,接着去求 前缀和 ,当所有区间内所有值都等于 0 的时候,说明猜测正确,但是我们还是要 缩小 猜测值,再去查找。当大于 0 的时候,说明猜测的值过小了,增大猜测值,反之则过大,要缩小。
求 区间值 的时候还可以进行优化,我们没有必要去求全部的区间, [s, t] 这个区间的值我们可以简化为 整个区间的最大的值 ,当最大值大于 0 的时候,就说明我们的猜测错误了,猜测值需要修改。
以这个点为结尾的 长度 为 [s, t] 区间的最大值又可以转化为这个点的前缀和的值减去前面 [s, t] 区间的最小值,最小值的实现我们可以用 单调队列 ,这样的话就将时间复杂度降为 O(n)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, t, s;
double l, r;
vector <double> num(maxn), sum(maxn);
bool judge(double x){
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + num[i] - x;
deque <int> q;
for (int i = 0; i <= n - s ; i++){
while (!q.empty() && sum[i] < sum[q.back()]) q.pop_back();
q.push_back(i);
while (!q.empty() && q.front() + t - s < i) q.pop_front();
if (sum[i + s] > sum[q.front()]) return false;
}
return true;
}
int main(){
cin >> n >> s >> t;
for (int i = 1; i <= n; i++) scanf("%lf", &num[i]);
l = -1e4, r = 1e4;
while (r - l > 1e-5){
double mid = (l + r) / 2;
if (judge(mid)) r = mid - 0.00001;
else l = mid + 0.00001;
}
printf("%.3lf\n", l);
return 0;
}