# [COCI2014-2015#7] PROSJEK

## 题目描述

给定一个有 $n$ 个整数的数列 $a$。请找出一个长度至少为 $k$ 的连续子序列,使这个子序列的所有数的平均值最大。

## 输入格式

输入共 $2$ 行。

第一行输入两个整数 $n,k$。  
第二行输入 $n$ 个整数 $a_1,a_2,\dots,a_n$,表示数列 $a$ 的所有数。

## 输出格式

输出仅一行,一个实数,表示长度至少为 $k$ 的连续子序列的数的最大平均值。

在每个测试点中,只要你的程序给出的答案和标准答案的相对误差不超过 $10^{-3}$,你的程序就可以通过该测试点。

## 样例 #1

### 样例输入 #1

```
4 1
1 2 3 4
```

### 样例输出 #1

```
4.000000
```

## 样例 #2

### 样例输入 #2

```
4 2
2 4 3 4
```

### 样例输出 #2

```
3.666666
```

## 样例 #3

### 样例输入 #3

```
6 3
7 1 2 1 3 6
```

### 样例输出 #3

```
3.333333
```

## 提示

**【数据范围】**

对于 $30\%$ 的数据,保证 $n\leqslant 5000$。  
对于所有数据,$1\leqslant k\leqslant n\leqslant 3\times 10^5$,$1\leqslant a_i\leqslant 10^6$。

一道洛谷的题目,题目意思很简单,就是在一个长度为n的数字串里面找出一个长度至少为k的,使得这个字串的平均值尽量大。

尽量大这种题一般是要用二分,但是简单二分长度似乎并不可行,因为短的不一定小,长的也不一定大,所以没有单调性,不能而二分长度,那么考虑二分答案。

先二分答案,再进行验证,只要能够找到这样一个答案,那么就可以直接返回,继续寻找更大的。这里主要就是验证的过程中,对数组的一些处理.一串数字的均值大于一个数,简便起见,可以转化为这一串数字,每个数都减去这个数之后的和大于0。同时因为长度一定是大于等于k的,并且这一串数字一定是连续的,假设当前的右端点位于i位置,那么i加上前面一共k个数一定是在数字串内的,所以就需要在1--i-k中找到一个分界点 j ,将 j -- i 这段区间的平均值看做 i 作为右端点,均值最大的区间,验证是否sum[i]-sum[j]>0,因为我们需要的是验证,也就是存在性问题,所以一定是让sum[j]越小越好,动态更新 j 使得j始终存储最小值,在代码就直接记录数字,也就是sumx。

代码

#include<stdio.h>
#include<algorithm>
using namespace std;
#define eps 1e-6
#define inf 0x3f3f3f3f
#define ll long long
double sum[40000];
double a[40000];
double b[40000];
int n, k;
int check_if(double t) {
	//sum[0] = 0;
	for (int i = 1; i <= n; i++) {
		b[i] =a[i] - t;
		sum[i] = sum[i - 1] + b[i];
		//预处理,每个数前减去二分的同时计算前缀和
	}
	double minx = inf;//标记(i-k+1)项的最小值
	for (int i = k; i <= n; i++) {
		if (sum[i] - minx >= 0)return 1;
		minx = min(minx, sum[i - k+1]);//标记,注意这里要+1是因为这个minx是要给下一位的用
		//printf("%.2lf\n", minx);
	}
	return 0;

}
int main()
{
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; i++) {
		scanf("%lf", a + i);
	}
	double l=0, r=inf;
	while ( r - l > eps) {
		double m = (r + l) / 2;
		if (check_if(m)) {
			l = m;//继续增大
		}
		else {
			r = m;//减小
		}
		//printf("%lf\n", m);
	}
	printf("%.6lf", l);
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值