Jzoj 4256NOIP2015模拟10.20二分贪心平均数

Posted SSL_ZZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jzoj 4256NOIP2015模拟10.20二分贪心平均数相关的知识,希望对你有一定的参考价值。

【NOIP2015模拟10.20】平均数

Link

Jzoj【4256】【NOIP2015模拟10.20】平均数


题面

Description
给出包含一个N个整数的数组A。找出一段长度至少为K的连续序列,最大化它的平均值。
请注意:一段子序列的平均值是子序列中所有数的和除以它的长度。

Input
第一行包含两个整数N(1<=N<=300000),K(1<=K<=N)。
第二行包含N个整数,代表数组A,1<=ai<=10^6。

Output
一行一个实数,代表最大的平均值。允许在0.001以内的绝对误差。

Sample Input
输入1:

4 1
1 2 3 4

输入2:

4 2
2 4 3 4

输入3:

6 3
7 1 2 1 3 6

Sample Output
输出1:

4.000000

输出2:

3.666666

输出3:

3.333333

Data Constraint
对于30%的数据,N<=5000。
对于100%的数据,1<=N<=300000, 1<=K<=N, 1<=Ai<=1000000。


解题思路

二分平均数 + 贪心维护

每次二分出平均数,每个数减去平均数
如果有一段(一个数也算长度为1的一段)的和≥0,那么这个平均数肯定是合法的

做一个前缀和,然后用一个变量标记前面最小的负数
每次用前缀和 - 前面最小的负数 就可以就求出 以当前点为结尾 的和最大的一个段的和
why?因为负数只会对答案产生不利效果,正数才会产生优好结果,所以减去最小负数就可以算出最优结果


Code

#include <iostream>
#include <cstdio>
#define ldb long double
#define N 300100
#define eps 1e-10

using namespace std;

int n, k, a[N];
ldb b[N], ans, l, r;

int check(ldb x) {
	for(int i = 1; i <= n; i++)
		b[i] = b[i - 1] + (ldb)a[i] - x;  //减去平均数,算前缀和
	ldb minn = 0;  //标记前面最小的负数
	for(int i = k; i <= n; i++) {  //长度至少为k噢
		if(b[i] - minn >= 0) return 1;  //前缀和 - 前面最小的负数
		if(b[i - k + 1] < minn) minn = b[i - k + 1];
	}
	return 0;
}

int main() {
	freopen("average.in", "r", stdin);
	freopen("average.out", "w", stdout);
	scanf("%d %d", &n, &k);
	for(int i = 1; i <= n; i ++)
		scanf("%d", &a[i]), r += (ldb)a[i];
	while(r - l > eps) {
		ldb mid = (l + r) / 2.0;
		if(check(mid)) l = mid, ans = mid;
			else r = mid;
	}
	printf("%Lf", ans);
}

以上是关于Jzoj 4256NOIP2015模拟10.20二分贪心平均数的主要内容,如果未能解决你的问题,请参考以下文章

[jzoj]4216.NOIP2015模拟9.12平方和

[贪心][前缀和] Jzoj P4256 平均数

[jzoj]2938.NOIP2012模拟8.9分割田地

Jzoj 3055NOIP2012模拟10.27期望比赛

Jzoj 3054NOIP2012模拟10.27倍增祖孙询问

Jzoj4742NOIP2016提高A组模拟9.2快速幂单峰