HDU-2993 二分-至少连续k个平均值最大(数形结合)

题目连接

分析讲解

题意:在n个序列中找出至少k个连续的数,使它们平均值最大,输出最大的值。

题意转化:用sum()求出前n个数的和,那么p=(sum(j)-sum(i-1))/(j-i+1) ,j-i>=k+1;把sum(i)看做y轴的值,i看做x轴的值,由于ai>0;所以图形是:

一条递增的凹凸不平的折线。那么问题就是:在这条折线上找出横坐标的值至少相差k,他们的之间的连线斜率最大。

个人分析:

先以为此题目很容易,没想到自己的算法是暴力枚举O(n^2),由于数据过大,肯定是过不了的。

听别人说斜率优化!,看了讲解,慢慢弄懂了。

斜率优化有两个重点:

                     1.排除多余的凹点(以下为准)。

                      2.二分找最凸点

排除凹点:


画图就能看出了凹点不可能是去最大值,除了凹点,对结果无影响。把所有的凹点都筛选出来了,留下的当然构成一个递增凸函数。

可以看出,最凸的点排列情况,是一个凸函数,故可以用二分搜索得到!



此题有点坑!时间卡的很厉害,用scanf输入都过不了!

代码:

#include<iostream>
#include<cstdio>

typedef __int64 LL;
using namespace std ;

struct point{  //用pair可能要快些。
   int x;
   LL y;	
}p[100005];

int n,k;
LL a[100005];

bool cross(point u,point v,point w)
{
	return (v.x-u.x)*(w.y-v.y)<(v.y-u.y)*(w.x-v.x);
}

inline int search(int l,int r,point G)
{
	int mid;
	while(l<r){
	    mid=(l+r)>>1;
		if(cross(p[mid],p[mid+1],G)) r=mid;
		else l=mid+1;
	}
	return l;
}

double solve()
{
	double Max=-1;
	int cnt=0;
	point L,R;
	for(int i=k;i<=n;i++){
		 L.x=i-k,L.y=a[i-k];
		 R.x=i,R.y=a[i];
		while(cnt>1&&cross(p[cnt-1],p[cnt],L)) cnt--;
		p[++cnt]=L;
		int o=search(1,cnt,R);
		double ans=(double)(a[i]-p[o].y)/(i-p[o].x);
		if(ans>Max) Max=ans;
	}
	return Max;
}

inline int input()
{
    char ch=' ';
    while(ch<'0'||ch>'9')ch=getchar();
    int x=0;
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
    return x;
}

int main()
{
    while(scanf("%d %d",&n,&k)!=EOF){
		a[0]=0;
		for(int i=1;i<=n;i++){
			a[i]=(LL)input();
		    a[i]+=a[i-1];
		}
		printf("%.2lf\n",solve());
    }
 	return 0 ;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值