USACO 2003 March Green - Best Cow Fences

· · 题解

题目

Farmer John's farm consists of a long row of N (1 <= N <= 100,000)fields. Each field contains a certain number of cows, 1 <= ncows <= 2000.

FJ wants to build a fence around a contiguous group of these fields in order to maximize the average number of cows per field within that block. The block must contain at least F (1 <= F <= N) fields, where F given as input.

Calculate the fence placement that maximizes the average, given the constraint.

链接(to poj)

翻译

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

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

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

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

链接(to acwing)

题解

首先数学化题目。他是想说:

对于一个数列 A=(a_1,a_2,...,a_N) ,求

\max_{l,r\in[1,N],r-l+1\ge F}\{{\frac{1}{r-l+1}\cdot \sum_{i=l}^r {a_i}}\}

从数据范围可知复杂度应当低于 O(n\ log\ n) 。首先考虑二分出平均数,再判断是否存在一个子段的平均数大于等于这个平均数。但是仔细一想,发现 “子段的平均数” 这个东西很难解决:它是比值定义式,既受总和影响,又被总数制约。而二分答案的 check 函数判断只能有一个限制。怎么办呢?

我们跳过比值定义式,从平均数的“移多补少”原理思考:我们把每个元素减去平均数,结果就删去了问题中的“平均数”字符串,发现现在要判断的问题变为:是否存在一个子段和非负。子段和……好解决啊!只需要考虑最大子段和即可,用双指针解决。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath> //poj 不支持万能头……
using namespace std;
int n,f,c[100005];
double s[100005],l,r,mid;
bool check(double ave){
    double mn=0;    //mn 记录了最小前缀和…未必是第一个(有负数)!
    for(int i=1;i<=n;i++)s[i]=s[i-1]+c[i]-ave;
    for(int i=0,j=f;j<=n;i++,j++){  //双指针优化
        mn=min(mn,s[i]);
        if(s[j]-mn>=0)return true;
    }return false;
}
signed main(){
    scanf("%d%d",&n,&f);
    for(int i=1;i<=n;i++)scanf("%d",c+i),r=max(r,1.0*c[i]);
    while(r-l>1e-5){
        mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }printf("%d\n",int(r*1000));
    return 0;
}

思维方式

$$\frac {a_l+a_{l+1}+...+a_r}{r-l+1}\ge ave$$ $$\iff \frac {a_l+a_{l+1}+...+a_r}{r-l+1}-ave\ge 0$$ $$\iff a_l+a_{l+1}+...+a_r-ave\cdot (r-l+1)\ge 0(r-l+1\neq 0)$$ $$\iff (a_l-ave)+(a_{l+1}-ave)+...+(a_r-ave)\ge 0$$ 解决不了的问题转化成了可解决的!