Jzoj 4256NOIP2015模拟10.20二分贪心平均数
Posted SSL_ZZL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jzoj 4256NOIP2015模拟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二分贪心平均数的主要内容,如果未能解决你的问题,请参考以下文章