最佳牛围栏

题意


农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于 1 头,也不会超过 2000头。

约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。

围起区域内至少需要包含 F 块地,其中 F 会在输入中给出。

在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。

输入格式

第一行输入整数 N 和 F,数据间用空格隔开。

接下来 N 行,每行输入一个整数,第 i +1 行输入的整数代表第 ii 片区域内包含的牛的数目。

输出格式

输出一个整数,表示平均值的最大值乘以 1000 再 向下取整 之后得到的结果。

数据范围

1≤N≤100000
1≤F≤N

输入样例:

10 6
6 
4
2
10
3
8
5
9
4
1

输出样例:

6500

分析


平均值最大化涉及到多个变化因素,我们可以先对每个数字减去一个平均值,先将原问题转化为给定一个平均值,是否存在一段区间使得该区间和大于等于0。

这个问题存在两段性,即当某个平均值是可行解时,左区间全部满足条件,右区间一部分满足条件。因此我们可以用浮点数二分解决。

第二个难点是如何快速判断长度大于等于F的区间和是否大于0,显然如果枚举区间是\(O(n^2)\)会超时。仔细分析就知道,我们可以在\(O(n)\)的时间复杂内解决。

  • 首先先求出每个数字减去平均值的前缀和
  • 当一个区间的右端点是\(i\)时,左端点的可选范围是\(j\in[0, i-F+1]\)(保证包括两端点的区间长度至少为F),第二个问题对应于求$min(s(i)-s(j)) \(,我们可以遍历的时候顺便维护\)s[0] 至 s[i-F]\(的最小值\)minv$
  • 对于每个右端点,判断\(s[i]-minv>=0\) 是否成立即可

代码


#include<iostream>
using namespace std;

const int N=100010;
double a[N],s[N];
int n,m;

bool check(double x)
{
    for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i]-x;
    double mins=0;
    for(int i=m;i<=n;i++)
    {
        mins=min(mins,s[i-m]);
        if(s[i]-mins>=0)return true;
    }
    return false;
}

int main()
{
    scanf("%d%d",&n,&m);
    double l=0,r=0;
    for(int i=1;i<=n;i++){scanf("%lf",&a[i]);r=max(r,a[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;
}
posted @ 2021-03-12 12:33  冰糖ryj  阅读(121)  评论(0编辑  收藏  举报