## 题目描述
给定一个有 $n$ 个整数的数列 $a$。请找出一个长度至少为 $k$ 的连续子序列,使这个子序列的所有数的平均值最大。
## 输入格式
输入共 $2$ 行。
第一行输入两个整数 $n,k$。
第二行输入 $n$ 个整数 $a_1,a_2,\dots,a_n$,表示数列 $a$ 的所有数。
## 输出格式
输出仅一行,一个实数,表示长度至少为 $k$ 的连续子序列的数的最大平均值。
在每个测试点中,只要你的程序给出的答案和标准答案的相对误差不超过 $10^{-3}$,你的程序就可以通过该测试点。
## 样例 #1
### 样例输入 #1
```
4 1
1 2 3 4
```
### 样例输出 #1
```
4.000000
```
## 样例 #2
### 样例输入 #2
```
4 2
2 4 3 4
```
### 样例输出 #2
```
3.666666
```
## 样例 #3
### 样例输入 #3
```
6 3
7 1 2 1 3 6
```
### 样例输出 #3
```
3.333333
```
## 提示
**【数据范围】**
对于 $30\%$ 的数据,保证 $n\leqslant 5000$。
对于所有数据,$1\leqslant k\leqslant n\leqslant 3\times 10^5$,$1\leqslant a_i\leqslant 10^6$。
一道洛谷的题目,题目意思很简单,就是在一个长度为n的数字串里面找出一个长度至少为k的,使得这个字串的平均值尽量大。
尽量大这种题一般是要用二分,但是简单二分长度似乎并不可行,因为短的不一定小,长的也不一定大,所以没有单调性,不能而二分长度,那么考虑二分答案。
先二分答案,再进行验证,只要能够找到这样一个答案,那么就可以直接返回,继续寻找更大的。这里主要就是验证的过程中,对数组的一些处理.一串数字的均值大于一个数,简便起见,可以转化为这一串数字,每个数都减去这个数之后的和大于0。同时因为长度一定是大于等于k的,并且这一串数字一定是连续的,假设当前的右端点位于i位置,那么i加上前面一共k个数一定是在数字串内的,所以就需要在1--i-k中找到一个分界点 j ,将 j -- i 这段区间的平均值看做 i 作为右端点,均值最大的区间,验证是否sum[i]-sum[j]>0,因为我们需要的是验证,也就是存在性问题,所以一定是让sum[j]越小越好,动态更新 j 使得j始终存储最小值,在代码就直接记录数字,也就是sumx。
代码
#include<stdio.h>
#include<algorithm>
using namespace std;
#define eps 1e-6
#define inf 0x3f3f3f3f
#define ll long long
double sum[40000];
double a[40000];
double b[40000];
int n, k;
int check_if(double t) {
//sum[0] = 0;
for (int i = 1; i <= n; i++) {
b[i] =a[i] - t;
sum[i] = sum[i - 1] + b[i];
//预处理,每个数前减去二分的同时计算前缀和
}
double minx = inf;//标记(i-k+1)项的最小值
for (int i = k; i <= n; i++) {
if (sum[i] - minx >= 0)return 1;
minx = min(minx, sum[i - k+1]);//标记,注意这里要+1是因为这个minx是要给下一位的用
//printf("%.2lf\n", minx);
}
return 0;
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%lf", a + i);
}
double l=0, r=inf;
while ( r - l > eps) {
double m = (r + l) / 2;
if (check_if(m)) {
l = m;//继续增大
}
else {
r = m;//减小
}
//printf("%lf\n", m);
}
printf("%.6lf", l);
return 0;
}