• 算法进阶指南二分章节的两道题


    A.题意:给定个数为N的数列,从中挑一些不小于L的连续子段,求这些子段当中的数平均值最大是多少?

    思路:二分平均值转化为判定。我们直接去求这个>=L的子段当中的最大平均值比较难求。所以我们可以用二分的方法枚举mid,然后在判定这个mid是否合法。

    判定方法为,是否存在一个长度大于L的连续子段它的平均值>=mid。如果不存在,说明以mid为平均值取大了。则取r=mid。

    对平均值的处理有一种特殊方法,另每一个数都减去mid,则判定方法就转化为是否存在一个长度>=L的连续子段,使得每个数的和非负(最大的都>=0,则 一定存在)。

    求某一连续子段的方法,利用前缀和来求解,eg sum[ i ] - sum [ j ]就是求  j 到 i 的连续子段和

    另外用双指针来求>=L的区间的最大和。因为要保证长度是大于L的 所以i要从L开始往后递增,而  j  从   i-L  开始往前递减, sum[  i ]  -  sum [ j ]就保证了一个大于L的区间。但是由于i 从L往后每递增一个, j 的可选择空间也就增加一个,j的初始空间只有一个选择,而这道题判定方法问存不存在,所以要求我们求连续子段的最大的和,于是我们只需要保存前  i - L 当中最小的那个前缀和就可以了。

    下面上代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int maxn=1e5+6;
    const double eps=1e-6;
    int n,L;
    double cow[maxn];
    double sum[maxn];
    double b[maxn];
    
    bool check(double ave)
    {
        for(int i=1;i<=n;i++)
            b[i]=cow[i]-ave;
            
            sum[0]=0;
            for(int i=1;i<=n;i++)
            sum[i]=sum[i-1]+b[i];
            
            double ans=-1e10;
            double min_v=0;
            for(int i=L;i<=n;i++)
            {
                min_v=min(min_v,sum[i-L]);
                ans=max(ans,sum[i]-min_v);
            }
            if(ans>=0)
            return true;
            else
            return false;
    }
    
    
    
    int main()
    {
        cin>>n>>L;
        for(int i=1;i<=n;i++)
        //scanf("%lf",&cow[i]);
        cin>>cow[i];
        
        double l=0,r=2000;
        while(r-l>eps)
        {
            double mid=(r+l)/2;
            if(check(mid)) l=mid;
            else r=mid;
        }
        
        printf("%d
    ",(int)(r*1000));
        return 0;
     } 
  • 相关阅读:
    个人项目-小学四则运算 “软件”之初版
    分布式版本控制系统Git的安装与使用
    第一次作业-准备
    结对项目-四则运算 “软件”之升级版
    第三次作业:个人项目-小学四则运算 “软件”之初版
    分布式版本控制系统Git的安装与使用
    第一次作业:准备
    结对项目-四则运算 “软件”之升级版
    个人项目-小学四则运算 “软件”之初版
    分布式版本控制系统Git的安装与使用
  • 原文地址:https://www.cnblogs.com/rainyskywx/p/10349568.html
Copyright © 2020-2023  润新知