二分答案-acwing-102. 最佳牛围栏

题目传送门:t​​​102. 最佳牛围栏 - AcWing题库高质量的算法题库icon-default.png?t=N7T8https://www.acwing.com/problem/content/104/

解题思路

        整体解析

        按照题目要求我们要找到一块连续的区域,使其里面每块地里面的平均值最大,且这块区域的长度要大于f

        二分处理

        “平均值可能的最大值”二分的关键词,所以考虑用二分去解决。又因为数据无序且不可排序(要找的是连续的地,排完序后再原数组不一定连续),而且平均值是小数,所以考虑用实数二分猜测平均值,check函数判断。l = 0,r = 2000(其实跟精确可以是牛最多的那块地牛的数量),实数二分边界无需判定,l = mid,r = mid即可。

        check函数编写

        要判断的是是否存在一块区域,里面地的平均值>=mid(即实数二分猜测的平均值)。这里有2个处理技巧:

                1、为了快速确定一块区间的平均值,可以采用前缀和的方式(设s为前缀和数组),s里面存储的为1-i区间的平均值,s[i] = s[i-1]+a[i]-mid。如果平均值>=mid(在s数组中的体现就是s[i]>0)且长度>=f,说明当前区域平均值合法,return true,否则return false。

                2、为了快速找到合法的值可以采用,可以尽量找平均值最大的区间。如何找呢?涉及到一些dp的思想,就是找到当前判断区间的之前区间的最小值,而当前区间-这个最小值,便可能尽量大。如果发现s[i]-min >= 0,且区域长度>=f的话,说明合法 ,否则不合法

        输出处理

        输出r*1000,但不要是l*1000,因为会涉及到精度问题所以l*1000会和正确答案有所偏差

代码

   

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6+10;
double a[N],s[N]; //a为牛数量数组,s为平均数前缀和数组 
int n,f;
double l = 0,r = 0,mid;

bool check(double x){
	for(int i = 1; i <= n; i++){
		s[i] = s[i-1]+a[i]-x; //求平均值前缀和数组 
	}
	double minn = 0; //0~i-1区间的最小区间 
	for(int i = f; i <= n; i++){
		minn = min(minn,s[i-f]); //dp思想维护minn最小值 
		if(s[i] >= minn) return 1; //s[i]>=minn,实际含义就是s[i]-minn >= 0,看看这个区间里的平均数是否大于等于猜测的mid,也就是判断是否合法 
	}
	return 0;
}

int main(){
	cin >> n >> f;
	for(int i = 1; i <= n; i++){
		scanf("%lf",&a[i]);
		r = max(r,a[i]); //牛数最多的地即为r 
	}
	while(r-l > 1e-5){ //二分实数的精度为le-5 
		mid = (l+r)/2;
		//实数二分无边界 
		if(check(mid)) l = mid;
		else r = mid;
	}
	//输出r*1000而不是l*1000,否则会涉及到精度偏差 
	printf("%d",int(r*1000));
	return 0;
} 

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值