最大平均子序列

这道题的大概意思是给出两个数m,n,之后的m行都给出一个整数,让找出一个子序列长度大于等于n,且其平均值最大。这样的水题都要想半天,我好菜啊T^T,菜归菜还是要好好学习的。

分析

这道题的主要思路呢就是转换和二分,将求平均数转换成求和并用二分法逐步缩小最大平均值的范围,
最后当范围缩小至1e-5时即可认为是最大平均值。
首先先找到数组中最大的数max,以max/2为二分开始的起点(我们可以知道平均数一定不会超多最大的数),
那么如何判断平均数的范围呢,那么就要看平均数是怎么求得的,avg=(sum[i]-sum[j])/i-j,其实我们可以认为
平均数是斜率。那么寻找斜率最大(满足要求的)的不就好了吗?m是最短的长度那么寻找的斜率至少得满足条件
i-j>=m,那么找到sum最小的值(在条件范围内)并逐步和满足要求的其余的值进行比较即可,如果寻找过程中找到
平均值大于avg(即(sum[i]-sum[j])/i-j>avg)那么该数组的最大平均值大于avg,就可以缩小二分的范围并继续上一步操作。
反之,如果寻找的没有比avg大的,那么该数组的最大平均值小于avg,也可以缩小二分的范围并继续上一步操作。直到二分的范围缩小到满足误差之后即可停止。

代码

//最长平均子序列
#include <bits/stdc++.h> //万能头文件
using namespace std;
const int MAXN = 1e5;
int arr[MAXN + 5];
double sum[MAXN + 5];
int n, m;

bool check(double avg)
{
    for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + arr[i] - avg;
    double minv = 0;
    for(int i = m; i <= n; ++i) {
        minv = min(minv, sum[i - m]);
        if(sum[i] >= minv) return true;
    }
    return false;
}

int main()
{
    scanf("%d %d", &n, &m);  // cin  cout
    double l = 0, r = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", arr + i);
        r = max(r, double(arr[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;
}

猜你喜欢

转载自blog.csdn.net/qq_43064070/article/details/87210226