Description
给定一个长度为 n 的序列 a,定义 ai 为第 i 个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在[S,T] 之间的连续序列。最有价值段落是指平均值最大的段落。
段落的平均值 等于 段落总价值 除以 段落长度。
Input
第一行一个整数 n,表示序列长度。
第二行两个整数 S 和 T,表示段落长度的范围,在[S,T] 之间。
第三行到第n+2 行,每行一个整数表示每个元素的价值指数。
Output
一个实数,保留3 位小数,表示最优段落的平均值。
Sample 1
Inputcopy | Outputcopy |
---|---|
3 2 2 3 -1 2 | 1.000 |
AC代码
#include <iostream>
#define eps 1e-5 // 定义一个很小的数作为二分查找的精度
using namespace std;
const int N=1000005; // 定义数组的最大长度
int n,s,t; // n为数组长度,s和t为题目要求的区间大小
int a[N],q[N]; // a为输入的数组,q用于存储滑动窗口中的索引
double sum[N]; // sum[i]存储从a[1]到a[i]的元素减去mid的累加和
// 检查是否存在一个长度为t的子数组,其和大于等于0
bool check(double mid){
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]-mid; // 计算累加和并减去mid
int tail=0,head=1; // 双端队列,head为队首,tail为队尾
for(int i=s;i<=n;i++){ // 从s开始遍历,因为需要s长度的前缀和
while(head<=tail&&sum[q[tail]]>sum[i-s]) tail--; // 维护队列的单调性,保证队尾元素最大
q[++tail]=i-s; // 将新的位置加入队列
if(head<=tail&&q[head]<i-t) head++; // 如果队首元素已经不在当前考虑的t长度范围内,则出队
if(head<=tail&&sum[i]-sum[q[head]]>=0) return true; // 如果当前位置与队首位置的差值(即子数组和)大于等于0,则返回true
}
return false;
}
int main(void){
cin>>n>>s>>t; // 输入n, s, t
for(int i=1;i<=n;i++) cin>>a[i]; // 输入数组a
double l=-10000,r=10000; // 初始化二分查找的左右边界
while(r-l>eps){ // 当二分查找的区间大小大于eps时继续查找
double mid=l+(r-l)/2; // 计算中点
if(check(mid)) l=mid; // 如果存在满足条件的子数组,则调整左边界
else r=mid; // 否则调整右边界
}
printf("%.3f",l); // 输出最终的结果,即满足条件的最小子数组和
return 0;
}