POJ2018 最大平均值问题

题意:给定一个长度为N的数组,求其中长度大于等于K的连续的一段,使其算数平均值最大。输出最大平均值*1000取整的结果。

斜率斜率斜率。。大致感觉可以用斜率来做。。我试了试把最优决策的选择化成斜率的式子,但发现这本来就是斜率的式子,于是就不知道怎么弄了。。何老师讲的类似分数规划的方法来做。。但是我觉得斜率也应该可行,就上黑板去楚了三个点点,大致说清楚了上凸包的中间的一个点对于最后的答案不会有任何贡献。。然后何老师就让我下来写一下。。

正儿八经的题解:处理成前缀和,然后将下标作为x轴,前缀和作为y轴,两个下标之间的平均值就对应了这两个点之间的斜率,求最大的斜率即可。在草稿纸上画三个形成上凸包的点,两两之间连线,把平面分成三个区域,每个区域中的点的最大斜率的决策点要么是这三个点中最左边的,要么是最右边的,也就是说在上凸包上的点永远不可能有贡献。

那么按照斜率优化的做法,单调队列维护一下就行了。单调队列保存好的就是一个斜率递增的曲线,找到这个曲线和新加入的点的切线斜率即是最优值。每相邻两个点之间的斜率减去到新加入的点的斜率的值是递增的,最接近零的就是切线了,所以可以二分查找。实际过程中发现这个切点永远会比上一次的切点靠右,于是对对首进行单调操作即可均摊O(1)实现。

上学期和暑假做了一些斜率优化的题,都是按照模式进行的,没怎么真正理解。这次碰到斜率优化的鼻祖题,感觉那些比较复杂的操作模式反而不好针对这个太原始的题了,于是就用最原始的办法来推了一下。。于是我大致想明白为什么以前那些斜率优化可以直接毫不顾忌地删对首删队尾了。。


由于这题精度要求不高,比较斜率的时候直接用实数除法了。poj上过了,cqbzoj上莫名超时。

#include
#include
#include
using namespace std;
#define DB double
#define Max(a,b) ((a)>(b)?(a):(b))
const int MAXN = 100010;
DB a[MAXN];
int N, K, q[MAXN];

inline DB slope(int i, int j) {
	return (a[j] - a[i]) / (j - i);
}

int main()
{
	int i, t, l=1, r=1, in;
	scanf("%d%d", &N, &K);
	for (i = 1; i<=N; ++i)
	{
		scanf("%d", &t);
		a[i] = a[i-1] + t;
	}
	q[r++] = 0;
	DB ans = 0;
	for (i = K; i<=N; ++i)
	{
		while (l+1 < r && slope(q[l], q[l+1]) < slope(q[l+1], i)) ++l;
		ans = Max(ans, slope(q[l], i));
		
		in = i - K + 1; //为了保证长度不低于K,每次将K个之前的点入队。
		while (l+1 < r && slope(q[r-2], q[r-1]) > slope(q[r-1], in)) --r;
		q[r++] = in;
	}
	ans *= 1000.0;
	t = ans;
	printf("%d\n", t);
	return 0;
}

你可能感兴趣的:(队列/斜率优化,思维,题解)