题目描述
给定一个长度为 n 的序列 a,定义ai 为第 i 个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在 [S, T] 之间的连续序列。最有价值段落是指平均值最大的段落。
段落的平均值 等于 段落总价值 除以 段落长度。
输入格式
第一行一个整数 n,表示序列长度。
第二行两个整数 S 和 T,表示段落长度的范围,在 [S,T] 之间。
第三行到第 n+2 行,每行一个整数表示每个元素的价值指数。
输出格式
一个实数,保留 3 位小数,表示最优段落的平均值。
思路:单调队列+二分法
单调队列是单调递减的在 s~t区间内,有平均数mid是为true;
用i - s遍历数组的i- s个数,在区间s~ t中找到最小的值即可。当满足s时就可以判断t,t是可以遍历数组满足s是区间不超过 t 是的数组区间
在队列中最多有 t 个元素 , i - t是保证元素在s~t之间的
#include<iostream>
#include<string>
#include<algorithm>
#include<deque>
using namespace std;
int n, s, t;
double a[100010], sum[100010];
int check(double mid) {
sum[0] = 0;
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i] - mid;
deque<int> q;
for (int i = 1; i <= n; i++) {
if (i >= s) {
while (!q.empty() && sum[i - s] < sum[q.back()]) q.pop_back();
q.push_back(i - s);
}
if (!q.empty() && q.front() < i - t) q.pop_front();
if (!q.empty() && sum[i] - sum[q.front()] >= 0) return 1;
}
return 0;
}
int main() {
cin >> n >> s >> t;
double m1 = 0x3f3f3f, m2 = -0x3f3f3f;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if(m1 > a[i])
m1 = a[i];
if(m2 <a[i])
m2 = a[i];
}
double l = m1, r = m2;
while (r - l > 1e-5) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid; // 平均数
}
printf("%.3lf\n", l);
return 0;
}