0
点赞
收藏
分享

微信扫一扫

二分,前缀和,双指针

后来的六六 2022-02-02 阅读 80

原题链接
参考题解

题意概述

在这里插入图片描述
在给定的10个数中,连续选择大于等于6个数,求最大的可能的平均值。

为什么想到二分

题目中给了一个条件,每个数不会超过2000。给出的数据范围是10w,只能用O(nlogn)的算法,只能用二分了。可以给出的序列又没有单调性。所以想到在0-2000以内,二分答案。
在这里插入图片描述还有一个trick,在代码的注释中说明了。真的牛!

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int cows[N];
double sum[N]; //前缀和数组

bool check(double avg)
{
    for (int i = 1; i <= n; i++ )
        sum[i] = sum[i-1] + cows[i] - avg;
    
    double mins = 0;
    // 因为i和j固定差m,所以可以用双指针
    // 可以少一层for循环
    // 这里还有一个优化,本来是要找i-m+1, 到i这几个数中
    // 这几个数的和sum是不是>=0, 现在利用前缀和,即是否有sum[i] >= sum[i-m+1]
    // 但是考虑到区间的长度选取还可以大于m,所以用双指针,mins记录最小值
    // 太妙了
    for (int i = m, j = 0; i <= n; i ++, j ++ )
    {
        mins = min(mins, sum[j]); // 记录前边的最小值
        if (sum[i] - mins >= 0) return true; 
    }
    
    return false;
}

int main()
{
    cin >> n >> m;
    double l = 0, r = 0;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> cows[i];
        r = max(r, (double)cows[i]);    
    }
    
    while (r - l > 1e-5)
    {
        double mid = (l + r) / 2;
        if(check(mid)) l = mid; //找满足check条件的最大值,找尽可能右边的数
        else r = mid;
    }   
    
    printf("%d\n", (int)(r * 1000));
    
    return 0;
}
举报

相关推荐

0 条评论