题目链接:Click here~~
题意:
给一个长度为 n 的序列,找出长度 >= k 的平均值最大的连续子序列。
解题思路:
斜率优化的例题。
先求出前缀和数组 sum[] ,然后问题转化成给出 n+1 个点求出两点横坐标差 >= k 的点对所能构成的最大斜率。
然后朴素的想法是对于每一个可能的序列末端点 i,去寻找集合 [1,j] (i-j+1 >= k)中与它能构成的最大斜率,不断更新最大值。复杂度 O(n^2)。
然后斜率优化的思想,是对于不断增长的集合 [1,j] 维护一个下凸曲线,每次找出曲线中的切线即为当前最优值。
找切线的时候,根据下凸曲线中切点斜率递增的性质,可以删除已经找到的切点之前的点。复杂度为 O(n)。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
typedef long long LL;
int a[N],sum[N],q[N];
bool useless(LL Xa,LL Xb,LL Xc){
return (sum[Xc] - sum[Xb]) * (Xb - Xa) <= (sum[Xb] - sum[Xa]) * (Xc - Xb);
}
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
}
int head = 0 , rear = -1;
double ans = 0;
for(int i=k;i<=n;i++)
{
while(head < rear && useless(q[rear-1],q[rear],i-k))
--rear;
q[++rear] = i - k;
while(head < rear && useless(q[head+1],q[head],i))
++head;
ans = max(ans,(sum[i] - sum[ q[head] ]) * 1.0 / (i - q[head]));
}
printf("%.2f\n",ans);
}
return 0;
}