题目描述
给定一个长度为n的序列a_i,定义a[i]为第i个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在[S,T]之间的连续序列。最有价值段落是指平均值最大的段落,
段落的平均值=段落总价值/段落长度。
输入格式
第一行一个整数n,表示序列长度。
第二行两个整数S和T,表示段落长度的范围,在[S,T]之间。
第三行到第n+2行,每行一个整数表示每个元素的价值指数。
输出格式
一个实数,保留3位小数,表示最优段落的平均值。
输入输出样例
输入 #1
3
2 2
3
-1
2
输出 #1
1.000
说明/提示
【数据范围】
对于30%的数据有n<=1000。
对于100%的数据有n<=100000,1<=S<=T<=n,-10000<=价值指数<=10000。
【题目来源】
tinylic改编
还是那句话,一个bug越难找,往往这个bug就越低级。
区间长度为s的段落数之和我把sum[i] - sum[i - s] 写成了sum[i] - sum[i - s + 1]…找了好久的bug。。。
这道题关键是用单调队列维护一个长度为 s - t 的段落和的最大值。
看别人代码一直没看懂,,这时候电脑没电了给电脑充电的过程中思考如何维护段落的最大值,然后想明白了,也敲出来了。。何必看之前看别人代码呢?
算法,重要的是算法思想,而非代码本身。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
int que[maxn];
double sum[maxn];
int head, tail;
int n, m;
int s, t;
bool judge(double x) {
head = 0, tail = 0;
// cout << x << endl;
for(int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + (double)a[i] - x;
for(int i = s; i <= n; i++) {
while(head < tail && que[head] <= i - t) head++;
while(head < tail && sum[i - s] <= sum[que[tail - 1]]) tail--;
que[tail++] = i - s;
if(head < tail && sum[i] - sum[que[head]] >= -1e-5)
{
// cout << i << " " << que[head] << " " << sum[i] << " " << sum[que[head]] << endl;
return true;
}
}
return false;
}
int main() {
cin >> n >> s >> t;
for(int i = 1; i <= n; i++) cin >> a[i];
double l = -100000, r = 100000;
double ans = 0;
// cout << judge( 1.52588) << endl;
while(r - l > 1e-5) {
double mid = (l + r) / 2;
// cout << judge(mid) << " " << l << " " << r << " " << mid << endl;
judge(mid) ? ans = mid, l = mid : r = mid;
}
printf("%.3lf", ans);
return 0;
}