POJ 2018 Best Cow Fences(二分答案)

题目链接:http://poj.org/problem?id=2018

题目给了一些农场,每个农场有一定数量的奶牛,农场依次排列,问选择至少连续排列F个农场的序列,使这些农场的奶牛平均数量最大,求最大数量*1000/农场的个数。

思路:题目是求是否存在一个长度不小于F的子段,使得平均数最大。

1.用二分法从给定数据的最小平均数到最大平均数进行二分枚举,每次枚举的平均值为mid,那么子段的每一个元素减去mid值再求和应当大于0,否则不满足题意,这样可以不断地二分,最终找到最大的mid值

2.判断子段和可以利用前缀和,每次二分的时候,先行让每个元素减去mid值,再求一个前缀和sum[i],前缀和大于0则说明平均数大于mid

 

3.从 i = F开始枚举到i = N,每次都要记录当前0到(i - N)的最小前缀和minval,因为当sum[ i ] - minval时,才可能存在长度不小于F且子段和最大的序列。

4.因为是在实数域上的二分,所以要注意精度问题,精度eps一般取1e-(k+2),此题k为3(1000是10的3次方)

 

AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
double a[100002],b[100002],sum[100002]; 
int main(){
	int N,F;
	scanf("%d%d",&N,&F);
	for(int i = 1;i<=N;i++){
		scanf("%lf",&a[i]);
	}
	double l = -1e6, r = 1e6;
	while(l+1e-5 < r){//注意精度问题 
		double mid = (l + r)/2; 
		for(int i = 1;i<=N;i++){
			b[i] = a[i] - mid;//依次减去枚举的平均值mid 
		}
		for(int i = 1;i<=N;i++){
			sum[i] = (b[i] + sum[i-1]);//求前缀和 
		}
		double ans = -1e10,minVal = 1e10;
		for(int i = F;i<=N;i++){
			minVal = min(minVal,sum[i-F]);//记录当前最小前缀和
			                              //且保证子段大于等于F 
			ans = max(ans,sum[i] - minVal); //求子段最大和 
		}
		if(ans >= 0){
			l = mid ;
		}
		else{
			r = mid ;
		}
	}
	cout<<int(r*1000);
	return 0;
}

 

posted @ 2019-09-05 10:35  AaronChang  阅读(88)  评论(0编辑  收藏  举报