【题解 && 蓝书刷题 && 二分答案】 Best Cow Fenes

题目描述:

农场主 John (简称 FJ) 的农场有一长排的 N (1 <= N <= 100,000)块地组成. 每块地有一定数量 (ncows) 的牛, 1 <= ncows <=2000.
FJ 想修建环绕邻接的一组地块的栅栏, 以最大化这组地块中平均每块地中牛的个数.
这组地块必须包含至少 F (1 <= F <= N) 块地, F 作为输入给出.
给定约束, 计算出栅栏的布置情况以最大化平均数.


分析:

  • 暴力分很好拿,只需要搞一个前缀和便可以

想正解

首先我们仍可以处理出前缀和 s u m [ i ] sum[i] sum[i]表示前i个数的和
求一个字段和,可以转化为前缀和相减的形式:
m a x i − j ≥ L { A j + 1 + A j + 2 + … … + A i } = m a x { s u m i − m i n 0 ≤ j ≤ n { s u m j } } max_{i-j\geq L}\{A_{j+1}+A_{j+2}+……+A_i\}=max\{sum_i-min_{0\leq j \leq n}\{sum_j\}\} maxijL{Aj+1+Aj+2++Ai}=max{sumimin0jn{sumj}}

这时我们发现每当i增加1,j的相应值也只会增加1.
根据这个性质,我们便可以舍去一层循环,直接用一个变量记录当前的最小值即可,只要与新进来的数进行比较

这样便可以 O ( n ) O(n) O(n)求出最大子段和。

由于问题具有单调性(自己思考),我们便可以用二分答案来二分平均数,将数列中每个数都减去平均数求一边最大子段和看是否>0即可


Code

#include
using namespace std;
int n,m;
double a[1011000],b[101010],summ[101010];

int main(){
	freopen("cowfnc.in","r",stdin);
	freopen("cowfnc.out","w",stdout);
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
    double k=0.00000001,l=-1000000.000,r=1000000.000;
    while (r-l>k){
	    double mid=(l+r)/double(2);
	    for (int i=1;i<=n;i++) b[i]=a[i]-mid;
	    for (int i=1;i<=n;i++)
	      summ[i]=summ[i-1]+b[i];
	    double minn=1000000.000,ans=-10000000;
	    for (int i=m;i<=n;i++)
	      minn=min(minn,summ[i-m]),ans=max(ans,summ[i]-minn);
	    if (ans>=0) l=mid; else r=mid;
	}
	printf("%d",int(r*1000));
	return 0;
}

你可能感兴趣的:(题解,分治,蓝书刷题)