做了这题才发现和2011百度之星 初赛B C题 园艺布置 大概是一样的题目
题意:
给你一段数列, 求其长度不小于 K 的平均值最大的子序列。
分析:
令 F(i) 为以第 i 个数为结尾的列的最大平均值,可以写出DP方程:F(i) = max{[sum(i) - sum(j)] / (i - j)} (j < i, i - j >= k)就是
求水平距离大于等于 K 的两点,使其连线的斜率最大。
可以证明,i 的决策集合中的点,一定构成一个下凸折线;而 i 的最优决策,一定是点 (i, sum(i)) 与折线的切点。
资料见下:浅谈数形结合思想在信息学竞赛中的应用
//AC CODE:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 100005;
int n, k;
long long sum[N];
long long a[N];
struct Point
{
int x, y;
Point () {}
Point (int X, int Y)
{
x = X;
y = Y;
}
} Q[N];
long long get()
{
char c;
long long ret;
while (c=getchar(),c<'0'||c>'9');//处理非数值部分
ret=c-'0';//输入的第一个数据的首位
while (c=getchar(),c>='0'&&c<='9')
ret=ret*10+c-'0';
return ret;
}
inline int multi(Point p1, Point p2, Point p0)
{
//求矢量P[p0, p1], Q[p0, p2]的叉积
//p0是顶点
//叉积的一个非常重要性质是可以通过它的符号判断两矢量相互之间的顺逆时针关系:
//若 P × Q > 0 , 则P在Q的逆时针方向
//若 P × Q < 0 , 则P在Q的顺时针方向
//若 P × Q = 0 , 则P与Q共线,但可能同向也可能反向
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
int main()
{
int i;
while (scanf("%d %d", &n, &k) != EOF)
{
double ans = 0.0;
for (i = 1; i <= n; i++)
{
a[i] = get();
sum[i] = sum[i - 1] + a[i];
}
int f = 0, r = 0;
for (i = k; i <= n; i++)
{
Point now = Point(i - k, sum[i - k]);
while (r - f >= 2 && multi(Q[r - 1], now, Q[r - 2]) <= 0)//
r--;
Q[r++] = now;
//(sum[i] - Q[f].y)/(i - Q[f].x) <= (sum[i] - Q[f + 1].y)/(i - Q[f + 1].x)
while (r - f >= 2 && (sum[i] - Q[f].y) * (i - Q[f + 1].x) <= (sum[i] - Q[f + 1].y) * (i - Q[f].x))
f++;
ans = max(ans, double(sum[i] - Q[f].y) / double(i - Q[f].x));
}
printf("%.2lf\n", ans);
}
}