HDU2993 MAX Average Problem DP+斜率优化

题意:给定一个数字序列,定义ave(i,j)为从i到j的数字的平均值,要求j-i+1>=k,求最大的平均值。

解析:这个题可以转换一下,将sum[i]作为纵座标的值,那么就是求相距大于k的两点之间的斜率的最大值。(参考这篇文章:)可以知道加点的时候维护一个下凸的曲线,每次找出该点和曲线的切线即在该点的最大斜率。求斜率的时候,根据下凸曲线的性质,可以删去已找到的切点之前的点。

(吐槽一下:这题很卡输入,输入外挂都不管用,很恶心。看了评论区大神用fread的做法才过的)

代码(参考的大神代码):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005;
const int  MT = 25000000;
char IO_BUF[MT];
char *buf = IO_BUF;
int  sum[N], Q[N], tol;
inline void read(int &a){
	for(a = 0; *buf < 48;buf++);
	while(*buf > 47) a = a*10+*buf++-48;
}
bool useless(ll x, ll y, ll z){
	return 1LL*(sum[y]-sum[x])*(z-y) >= 1LL*(sum[z]-sum[y])*(y-x);
}
int main(){
	tol = fread(IO_BUF, 1, MT, stdin);
	int n, k;
	double ans;
	while(1){
		if(buf - IO_BUF + 1 >= tol)break;
		read(n);read(k);
		sum[0] = 0;
		ans = 0;
		for(int i = 1; i <= n; i++){
			read(sum[i]);
			sum[i] += sum[i-1];
		}
		int head = 0, rear = -1;
		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, 1.0*(sum[i]-sum[Q[head]])/(i-Q[head]));
		}
		printf("%.2f\n", ans);
	}
	return 0;
}
点赞