最大平均(和)子序列

来源:互联网 发布:mc9s12xs128单片机实验 编辑:程序博客网 时间:2024/06/16 05:46

描述
给定n个数的数组,找到所有长度大于等于k的连续子数组中平均值最大的那个。返回那个最大的平均值。
1 <= k <= n <= 10000,数组中的元素在范围[-10000, 10000]之间,最后返回的答案的误差应在10^(-5)以内。


样例
输入:[1,12,-5,-6,50,3], k = 4
输出:12.75


说明
长度为4的子数组中,最大的平均值为12.75,(=(12 + -5 + -6 + 50)/4)
长度为5的子数组中,最大的平均值为10.8,(=(12 + -5 + -6 + 50 + 3)/5)
长度为6的子数组中,最大的平均值为9.16667。(所有数的平均值)
因此返回12.75。


分析
首先,考虑把求平均问题转换为求和问题,实质上是一样的,然后用二分枚举,求出最大和。关键在于转换和二分,二分法此类题通用。求长度大于等于k的最大和子数组比原问题容易的多,令s为b的前缀和子数组,即s(i)=b(0)+b(1)+……+b(i-1),且s(0)=0,那么b(j)到b(i-1)的区间和可表示为s(i)-s(j)。
找长度大于等于k的最大和子数组等价于找i,j,满足i-j>=k,且使s(i)-s(j)最大。固定i,则要使s(i)-s(j)最大,s(j)应最小,同时也应满足j<=i-k,令p(i) = min{s(j)},j<=i-k,故 i 固定时s(i)-s(j)的最大值为s(i)-p(i),枚举所有i即可得到最终的最大值。
二分的初始区间可以设置为[min{a(i)},i=0~n-1 , max{a(i)},i=0~n-1],因为一组数的平均值不会小于这组数的最小值,也不会大于这组数的最大值。对于二分值A,通过前面讲的方法以O(n)的时间判断是否有子数组的平均值大于等于A,若有则答案大于等于A,若没有,则答案小于A。二分至区间长度小于所需精度,即可返回该值。


测试

#include<bits/stdc++.h>using namespace std;class Solution{public:    double find(const int n,int *num,int k){        double left=INT_MAX, right=INT_MIN;        for(int i=0;i<n;i++){            left=left<(double)num[i]?left:(double)num[i];            right=right>(double)num[i]?right:(double)num[i];        }        double sum[n+1];        while(right-left>1e-6){            double mid=(left+right)/2;            for(int i=0;i<n;i++){                sum[i+1]=sum[i]+num[i]-mid;            }            double preMin=0;            double sumMax=INT_MIN;            for(int i=k;i<=n;i++){                sumMax=sumMax>sum[i]-preMin?sumMax:(sum[i]-preMin);                preMin=preMin<sum[i-k+1]?preMin:sum[i-k+1];            }            if(sumMax>=0){                left=mid;            }            else{                right=mid;            }        }        return left;    }};int main(){    Solution sol;    int k,n;    cin>>n>>k;    int a[10001];    for(int i=0;i<n;i++){        cin>>a[i];    }    double result=sol.find(n,a,k);    cout<<result<<endl;    return 0;}

若需继续使用斜率优化,可以参考:

http://blog.csdn.net/lxc779760807/article/details/51366552

原创粉丝点击