链接:https://ac.nowcoder.com/acm/contest/1000/A
题目描述
农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过2000头。1≤N≤100000
约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。
围起区域内至少需要包含 F 块地。1≤F≤N
在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。
输入描述:
第一行输入整数 N 和 F ,数据间用空格隔开。
接下来 N 行,每行输入一个整数,第i+1行输入的整数代表第i片区域内包含的牛的数目。
输出描述:
输出一个整数,表示平均值的最大值乘以1000再 向下取整 之后得到的结果。
输入样例:
10 6
6
4
2
10
3
8
5
9
4
1
输出样例:
6500
解题思路:
- 前缀和 + 二分
二分最难的地方就在于 check 函数的写法。- ①:我们要找的是有没有一段不小于 F 的区间,使这段区间的平均数尽可能的大,如果我们
找到了一段连续的区间且区间长度不小于 F 且平均数大于我们二分的平均数 那么大于这个
数且区间也满足的一定满足了 我们直接判断正确即可。 - ②:因为我们要找一段区间的平均数,对于一段序列,每个数减去要找的的平均数,如果大
于 0 那么他本身就大于平均数,如果小于 0 那么它本身就小于平均数 此时我们就能算出
哪些数大于 0 哪些数小于 0 ,之后我们再使用前缀和,就能判断一个区间内的平均值是否
大于或小于我们二分的平均数了 - ③:因为我们不仅需要找 F 大小区间内,我们还要找>F 大小区间内的,我们可以使用双指
针的做法,即设 i=0,j=F 每次使两个数++ 因为 i,j 始终满足相距 F 的距离,所以我们用一个
变量 minv 来存储 i 所遍历到的最小值,这样我们比较的距离一定是≥F 的,并且如果我们用
j 位的前缀和数减去 minv 的话,就能得到我们的最优解,如果这个最优解>= 0 那么就满足
我们的指定条件
- ①:我们要找的是有没有一段不小于 F 的区间,使这段区间的平均数尽可能的大,如果我们
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
double sum[N];
int n, f;
bool check(double ans){
for(int i=1; i<=n; ++i) sum[i] = sum[i-1] + a[i] - ans; //计算前缀和
double minx = 0; //设置最小值
for(int i=0, j=f; j<=n; ++i, ++j){
minx = min(minx,sum[i]); //找最优极小值
if(sum[j] >= minx) return true; //进行判断,如果有满足的就返回 true
}
return false; //如果所有的都不满足,那么这个平均数就一定不满足
}
int main(){
scanf("%d%d",&n,&f);
for(int i=1; i<=n; ++i){
scanf("%d",&a[i]);
}
double l = 0, r = 2000;
while(r - l > 1e-5) {//开始二分 因为是实数所以这里加个精度
double mid = (l + r) / 2;
if(check(mid)) //将问题转变为判定问题
l = mid;
else
r = mid;
}
printf("%d\n",(int)(r * 1000)); //因为我们找的极大值 所以要右端点*1000
return 0;
}