最佳牛围栏

如题,这题是一个最优化的问题,暴力肯定会超时,就是上来用前缀和的话,因为地的数量大于等于F,所以最坏时间复杂度为o(N*(N-1))这个算法的话肯定也会超时,因为o(n^2)算法处理的数据只为5000,所以我们可以二分答案,就是最坏的时间复杂度是o(n*logn)能处理的数据为10^6,足够应对该题了。那么需要二分1到2000这个区间,这个问题变成了一个判定问题,判定问题其实比较好解,比mid=256,我们可以去判断原序列中存不存在一个区间的平均值大于mid,如果存在的话说明他的平均值可能有更大的,所以可以二分一下,去掉前半块区间。所以关键就在如何判定原序列中存不存在一个区间的平均值大于mid,这里有一个技巧,我们可以让原序列全都减去mid(avg),那么这个问题就变成了判定原序列中存不存在一个长度大于F的区间的平均值大于0,这里可以用到我们之前说的前缀和技巧,这里可以新加两个指针i和j指向这个区间,判断存不存在一个sum[j]-sum[i]>=0的一个区间(j-i>=F) ,那么我们又可以转换成这样,这里我们可以再加一个minv来记录i前面的区间中的最小值,如果这个最小值比sum[j]还小的话说明,sum[j]-sum[i]这个区间是大于0的。由此我们可以把这个问题求解出来。但是这个问题的时间复杂度是多少?,减去mid的时间复杂度是o(n),扫描再判断的时间复杂度是o(n),加上二分里面判断时间复杂度是o(n*logL),足够应对这题的数据范围。这题的思路是:1.先把最优化问题变成一个判定问题2.把每一个数都减去mid后,存不存在一个长度大于F的连续子序列它的总和非负,把问题变成一个非负性判断。3.用一个双指针的做法,第一个指针i在前,j在后,中间隔了F个距离,用一个变量记录i前面的最小值,最后判断sum[j]是不是大于前面的最小值就OK了。

下面是代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;

const int N=100010;
int cow[N];
int n,f;
double sum[N];
bool check(double avg)
{
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+cow[i]-avg;
    double minv=0;
    for(int i=0,j=f;j<=n;i++,j++)
    {
        minv=min(minv,sum[i]);
        if(sum[j]-minv>=0)return true;
    }
    return false;
}
int main()
{
    cin>>n>>f;
    for(int i=1;i<=n;i++)cin>>cow[i];
    double l=0,r=2000;
    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;
}

附上二分查找经典算法

二分答案模板

//整数(往下取整)
int search1(int l,int r)
{
while(left<right)
{
    int mid=left+right+1>>1;
    if(check(mid))left=mid;//check(mid)若真即为答案在右半部
    else right=mid-1; 
}
return l;
}
//双精度浮点型(往下取整)
int search2(int l,int r)
{
while(r-l<1e-6)
{
    double mid=(l+r)/2;
    if(check(mid))l=mid;
    else r=mid-1; 
}
return r;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值