最佳牛围栏(前缀和 + 二分)

题目描述
农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过2000头。

约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。

围起区域内至少需要包含 F 块地,其中 F 会在输入中给出。

在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。

输入格式
第一行输入整数 N 和 F ,数据间用空格隔开。

接下来 N 行,每行输出一个整数,第i+1行输出的整数代表,第i片区域内包含的牛的数目。

输出格式
输出一个整数,表示围起区域内每块地包含的牛的数量的平均值可能的最大值乘以1000得到的数值。

数据范围
1≤N≤100000
1≤F≤N
样例
输入样例:
10 6
6
4
2
10
3
8
5
9
4
1
输出样例:
6500

解题思路:首先我们可以这样理解,我们要寻找一段数列,这个数列满足,长度不小于F,并且它的子段和非负。也就是我们需要的二分判定。
二分:首先我们的mid=(l+r)/2mid=(l+r)/2,记住这里不要是>>1,因为这是浮点数除法。然后呢,我们可以进行前缀和运算,s[i]=s[i−1]+a[i]−mids[i]=s[i−1]+a[i]−mid,因为首先我们找的mid是这个平均值,其次前缀和,是为了处理子段和,比如说我要算出[3,5][3,5]的子段和,那么我们只需要输出s[5]−s[2]s[5]−s[2]即可。
这里呢,我们要找到这个满足题意的最优解[l,r][l,r],那么也就是说a[l−1]a[l−1]要尽量地小,然后a[r]a[r]要尽量地大,所以说我们就需要枚举这个ll,但是这样的话时间吃不消,那么怎么办呢?我们发现,每一次rr变大后,ll的取值范围从[1,l][1,l]变成了[1,l+1][1,l+1],所以说我们只需要一个变量,存储当前的最小值即可。具体可看代码实现。

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
#include <stdio.h>
#include <cmath>
#include <string.h>
#include <vector>

#define ll long long
using namespace std;

const int maxn = 100010;
int n, m;
double cows[maxn], sum[maxn];
double minv, ans;

bool check(double mid)
{
    for(int i = 1; i <= n ; i++)
        sum[i] = cows[i] - mid, sum[i] += sum[i - 1];
    minv = 1e8, ans = -1e8;
    for(int i = m; i <= n; i++)
    {
        minv = min(minv, sum[i - m]);
        ans = max(ans, sum[i] - minv);
    }
    return ans <= 0 ? false : true; 
}


int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> cows[i];
    double l = 1, r = 2000;
    while(r - l > 1e-5)
    {
        double mid = (l + r) / 2;
        if(check(mid)) l = mid;
        else r = mid;
    }
    printf("%d
" ,int(r * 1000)); 
    return 0;
}

  

原文地址:https://www.cnblogs.com/zxybdnb/p/11748571.html