欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

POJ 2018 Best Cow Fences(二分答案)

程序员文章站 2022-05-08 17:57:27
...

题目链接: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;
}