① 可能的最大值,抓住关键词,考虑是否可以用二分,假设存在某一个平均值满足题目条件,那么小于该值的平均值一定能满足题目条件,而大于该值的平均值不一定能满足题目条件,于是可得 可以用二分
② 为了快速判断某一区间的值是否大于我们二分的平均值,我们可以让每个数减去平均值,这样再将它们全部加起来就可以判断当前某一区间的平均值是否大于二分的平均值。
二分:首先我们的 mid=(l+r)/2mid=(l+r)/2mid=(l+r)/2,记住这里不要是 >>1>>1>>1,因为这是浮点数除法。然后呢,我们可以进行前缀和运算, s[i]=s[i−1]+a[i]−mids[i]=s[i−1]+a[i]−mids[i]=s[i−1]+a[i]−mid,因为首先我们找的 midmidmid 是这个平均值,其次前缀和,是为了处理子段和,比如说我要算出 [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] 要尽量地大,所以说我们就需要枚举这个 lll,但是这样的话时间吃不消,那么怎么办呢?我们发现,每一次 rrr 变大后,lll 的取值范围从 [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;
}
这篇博客介绍了如何利用二分查找策略来解决寻找数组中最大平均子数组的问题。通过预处理减去平均值的前缀和,可以在O(n log n)的时间复杂度内确定是否存在满足条件的子数组。文章详细阐述了算法的实现步骤,并提供了C++代码示例,演示了如何找到满足至少F块地的子数组,同时确保子数组的平均值大于或等于目标平均值。
6737

被折叠的 条评论
为什么被折叠?



