子序列的平均值(Java)

给定一个长度为n的非负序列A,请你找出一个长度不小于L的子段(子段是序列A中一些连续的元素构成的集合),使得子段中数值的平均值最大。最终输出这个最大的平均值。

输入格式:
第一行两个整数n,L(1<=L<=n<=100,000)

以下n行,每行一个非负整数,表示序列A中每个元素的值。

输出格式:
一个整数,欲求的最大平均值乘以1000后的结果(注意不要四舍五入,直接输出)。

输入样例:
10 6
6
4
2
10
3
8
5
9
4
1
输出样例:
6500

解析:

可以用二分答案的方法来做,一组数据的平均值一定在该组数据的最大值和最小值之间,那么我们可以通过二分法获得中间的数字mid,再判断是否有一组长度不小于L的子数组的平均值为mid,那么怎么来判断呢?又如何将二分进行下去呢?首先来说怎么判断子数组的平均值是否是mid,可以先让每个数字减去mid,然后再看是否有一组长度不小于L的子数组的大于等于0,如果有,那么说明该子数组的平均值要大于mid,这样一来二分就可以进行下去了,平均值大于mid,那么就要让二分法左边的值为mid,如果没有任何一组长度不小于L的子数组的大于等于0,那么说明平均值肯定要小于mid,这时候就要让二分法右边的值为mid,一直这样下去,直到二分法中右边的减去左边的大于1e-5,那么我们就可以认为右边和左边的相等,这时也就相当于平均值找到了。

具体代码如下:

import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		int[] a = new int[100000];
		double[] sum = new double[100000];
		int n, length;
		Scanner in = new Scanner(new BufferedInputStream(System.in));
		n = in.nextInt();
		length = in.nextInt();
		double l = 0, r = 0;
		for (int i = 1; i <= n; i++) {
			a[i] = in.nextInt();
			r = Math.max(r, (double) a[i]);// 找出该组数据中最大的
		}

		while (r - l > 1e-5) {
			double mid = (r + l) / 2.0;
			for (int i = 1; i <= n; i++) 
				sum[i] = sum[i - 1] + a[i] - mid;// 实际上包含两步,第一步每个数都减去平均值
				// 第二步前i个减去平均值后的数的总和存储在sum[i]中
				//这一步用来方便得到第i个到第j个的和,只需sum[j]-sum[i]就可以得到了
			int k=0;
			double min = 1e10;
			for ( k = length; k <= n; k++) {
				min = Math.min(min, sum[k - length]);
				if (sum[k] -min> 0) {//一旦找到有一个子数组平均值大于mid,就可以结束本轮遍历
					l = mid;
					break;
				}
			}
			if (k>n) //如果整个遍历完都没有的,说明平均值小于mid
				r = mid;
		}
		System.out.println((int) (r * 1000));
	}
}

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值