这个应该就是斜率优化了。(由于用到单调队列,好像也有叫单调队列优化的)
题目的意思很简单,给出N个正整数,求最大的,长度大于K的某一段的平均值。设部分和数组为S,则容易得到方程 (S[j]-S[i-1])/(j-i+1),这可以看做(i,S[i])和(j,S[j])的斜率,那么问题转化为求n个点中水平距离至少为k的最大斜率。
#include<stdio.h>
#define LL long long
typedef struct
{
int x,y;
}Node;
Node pp[100010];
Node tmp,now; double res; int sum[100010]; int head,tail;
LL multi(Node a,Node b,Node c)
{
return (LL)(b.x-a.x)*(c.y-a.y) - (LL)(b.y-a.y)*(c.x-a.x) ;
}
void read(int &r) {
char c;
while (c = getchar(), c < '0' || c > '9');
r = c - '0';
while (c = getchar(), c >= '0' && c <= '9') r = r * 10 + c - '0';
}
int main()
{
int n,k,i,j; double tt;
while(~scanf("%d%d",&n,&k))
{
sum[0]=0; head=tail=0; tt=res=0.0;
for(i=1;i<=n;i++)
{
read(sum[i]); //为什么要卡这个,我搞不清楚
sum[i]+=sum[i-1];
}
for(i=k;i<=n;i++)
{
tmp.x=i-k;
tmp.y=sum[i-k]; //对于每个新的点(i,sum[i]),会有新的点tmp要考虑是否加入到队列里
while( tail-head>=2 && multi(pp[tail-2],pp[tail-1],tmp)<=0 ) //判断新的点是否是当前凸包的点,是的话,去掉多余的点(tail--)
tail--;
pp[tail]=tmp; tail++; //加入新的点
now.x=i; now.y=sum[i];
while(tail-head>=2 && multi(pp[head],pp[head+1],now)>=0 ) // 这句话是要找到点p(i,sum[i])与折线的切点q( 切点的特征是K(q左边的点,q) < K(p,q) < K(q,q右边的点) ) 所以说p与q的斜率在一个固定的范围内,这个范围与点q有关。 至于为什么要把q前面的点删除呢,是因为下凸线的斜率是递增的,如果存在点S(s,sum[s])(s>i),S与折线的切点是q点之前的某个点T的话,那么 K(S,T)的斜率肯定小于K(q,p);为什么?前面已经解释了,是因为下凸线斜率的单调递增的性质决定的;所以说q点前的点都可以删除;
head++;
tt=(double)(now.y-pp[head].y)/(now.x-pp[head].x);
res= tt>res?tt:res;
}
printf("%.2lf\n",res);
}
return 0;
}