① 可能的最大值,抓住关键词,考虑是否可以用二分,假设存在某一个平均值满足题目条件,那么小于该值的平均值一定能满足题目条件,而大于该值的平均值不一定能满足题目条件,于是可得 可以用二分
② 为了快速判断某一区间的值是否大于我们二分的平均值,我们可以让每个数减去平均值,这样再将它们全部加起来就可以判断当前某一区间的平均值是否大于二分的平均值。
二分:首先我们的
m
i
d
=
(
l
+
r
)
/
2
mid=(l+r)/2
mid=(l+r)/2,记住这里不要是
>
>
1
>>1
>>1,因为这是浮点数除法。然后呢,我们可以进行前缀和运算,
s
[
i
]
=
s
[
i
−
1
]
+
a
[
i
]
−
m
i
d
s[i]=s[i−1]+a[i]−mid
s[i]=s[i−1]+a[i]−mid,因为首先我们找的
m
i
d
mid
mid 是这个平均值,其次前缀和,是为了处理子段和,比如说我要算出
[
3
,
5
]
[3,5]
[3,5] 的子段和,那么我们只需要输出
s
[
5
]
−
s
[
2
]
s[5]−s[2]
s[5]−s[2]
即可。
这里呢,我们要找到这个满足题意的最优解
[
l
,
r
]
[l,r]
[l,r],那么也就是说
a
[
l
−
1
]
a[l−1]
a[l−1] 要尽量地小,然后
a
[
r
]
a[r]
a[r] 要尽量地大,所以说我们就需要枚举这个
l
l
l,但是这样的话时间吃不消,那么怎么办呢?我们发现,每一次
r
r
r 变大后,
l
l
l 的取值范围从
[
1
,
l
]
[1,l]
[1,l] 变成了
[
1
,
l
+
1
]
[1,l+1]
[1,l+1],所以说我们只需要一个变量,存储当前的最小值即可。具体可看代码实现。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, F;
double a[N], s[N];
bool check(double avg)
{
//预处理减去了平均数后的前缀和
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i] - avg;
double mins = 0;
for (int k = F; k <= n; k ++ ) //因为这里要找至少F块地,所以可以取mins
{
mins = min(mins, s[k - F]); //储存最小的
if (s[k] - min >= 0) return true; //满足
}
return false;
}
int main()
{
scanf("%d%d", &n, &F);
double l = 0, r = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%lf", &a[i]);
r = max(r, a[i]); //找到右边界
}
while (r - l > 1e-5)
{
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%d\n", (int)(r * 1000));
return 0;
}