YbtOJ 二分算法课堂过关 例3 最大均值【二分】【前缀和】

在这里插入图片描述


思路

这道题显然需要二分一个平均值,
问题是小数怎么二分呢?
我们可以定义 l , r , m i d l,r,mid l,r,mid 为double类型,
并在判断时加上一个eps即可。(eps是在函数程序中事先声明的常量,是控制迭代精度的,相当于微积分里面的无限小值)

这个问题解决了,我们发现还有一个问题:
判断当前二分到的平均值是否满足条件是 O ( n 2 ) O(n^2) O(n2),会超时。
首先想到前缀和优化。
此时判断区间为 max ⁡ s u m i − s u m i − j + 1 ( l < i ≤ n , j ≤ i − l ) \max{sum_i-sum_{i-j+1}} (l<i≤n,j≤i-l) maxsumisumij+1(l<in,jil)
不难发现 j j j 只会在 1 1 1~ i − l i-l il 中循环比较,直接 max ⁡ \max max 即可,可以省掉一个for循环。
然后注意让当前区间的 s u m i − j + 1 sum_{i-j+1} sumij+1 最小即可得到较优答案。

C o d e Code Code

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double eps=1e-6;
double ans=0,a[100010];
int n,k;
bool check(double x)
{
    
    
	double b[100010];
	for(int i=1; i<=n; i++)
	   b[i]=b[i-1]+(a[i]-x);
	double minn=0,boss=-2147483647;
	for(int i=k; i<=n; i++)
	 {
    
    
	 	boss=max(boss,b[i]-minn);
	 	minn=min(minn,b[i-k+1]);
	 }
	if(boss>=0)
	  return 1;
	else
	  return 0;
}
int main()
{
    
    
	cin>>n>>k;
	for(int i=1; i<=n; i++)
	   scanf("%lf",&a[i]);
	double l=0,r=2000,mid=0;
	while(l+eps<=r)
	 {
    
    
	 	mid=(l+r)/2;
	 	if(check(mid))
	 	  l=mid,ans=max(ans,mid);
	 	else
	 	  r=mid;
	 }
	cout<<int(r*1000);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Jackma_mayichao/article/details/112062486