AC102 最佳牛围栏

题意大意就是:给定一个数列,问这个数列的长度大于F的子段的平均值最大是多少

如果直接遍历找到最大平均数的区间需要遍历两次,O(n^2),

但用有一种O(n)的办法可以判定存不存在一个区间,它的平均数超过某个数。

因此可以用二分搜索,在[0, 2000]的范围内搜索这个最大平均数。

而且题目也没有题目要求我们要去找到这个序列是什么,所以我们只需要判断是否有一个区间使得上述命题成立就行了,从而转换一个判定的问题

对于区间平均数,我们可以不需要每次都讲平均值计算一遍,我们可以把每个数减去他们所有的平均值,如果大于0就代表它比平均数大,这样使用一个前缀和我们就可以判断某个区间内的平均数是不是比原来的平均数大了

题目中要求的序列长度是要求比F要大的,我们可以采用双指针的算法;我们要看的是这个区间内的平均值能不能比当前的这个平均值要大,就是求这个操作(减平均数)之后的序列的某一段的和是不是要大于等于0

而我们看看每个大于F的区间有上述的命题成立,那么我们只需要让被减数尽可能的小就可以了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};

int n;
int f;
int a[100000 + 10];
double s[100000 + 10];

bool check(double av){
	for(int i = 1; i <= n; i++){
		s[i] = s[i - 1] + a[i] - av;
	}
	
	double minn = 20000;
	
	for(int i = 0, j = f; j <= n; j++, i++){
		minn = min(minn, s[i]);
		if(s[j] - minn >= 0){
			return 1;
		}
	}
	return 0;
}

int main(){
	cin >> n >> f;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	}
	
	double l = 1;
	double r = 2000;
	
	while(r - l > 1e-6){
		double mid = (l + r) / 2;
		if(check(mid)){ // 存在一个区间,使得他们的平均值大于等于mid
			l = mid;
		}
		else{
			r = mid;
		}
	}
	
	cout << int(r * 1000); // 如果答案是6500,那么l的取值虽然可以和r无限接近,但永远小于6500,所以取整之后就会得到6499,就错了。
	
	re 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值