题解 | #average#

average

https://ac.nowcoder.com/acm/contest/99072/H

题解:average

最大平均值子序列

问题描述

给定一个长度为 (n) 的序列,求出序列中一段连续子序列的最大平均值,且这个连续子序列的长度不小于 (k)。要求输出保留 6 位小数的最大平均值。

输入描述:

  • 第一行包含两个正整数 (n) 和 (k)。
  • 第二行包含 (n) 个整数表示这个序列。

输出描述:

  • 输出一个浮点数表示答案,保留 6 位小数。

示例1:

输入:

4 3 3 4 1 2

输出:

2.666667

示例2:

输入:

8 6 4 7 9 5 8 1 9 10

输出:

7.000000

备注:

  • 对于 100% 的数据,满足 (k \leq n \leq 10^5),且每个数 (a_i \leq 5000)。

解题思路

我们可以通过 二分查找前缀和 技巧来高效地解决这个问题。

1. 二分查找:

  • 我们的目标是找到一个子序列,其平均值最大,且长度不小于 (k)。
  • 由于平均值是一个实数,我们可以通过二分查找来逼近最大平均值。
  • 在每次的查找中,我们设定一个中值 (mid) 作为可能的最大平均值,然后判断是否存在一个连续子序列的平均值大于等于 (mid)。

2. 前缀和:

  • 使用前缀和来快速计算任意子序列的和。
  • 对于某个固定的 (mid),我们将每个元素减去 (mid),然后判断是否存在一个连续子序列,其和大于等于 0,且长度不小于 (k)。

3. 验证子序列是否满足条件:

  • 在给定的 (mid) 值下,创建一个新的数组,数组中的每个元素为原数组的元素减去 (mid)。
  • 然后,使用前缀和来检查是否存在一个长度至少为 (k) 的子数组,其和大于等于 0。

4. 终止条件:

  • 使用二分查找,当区间的左右边界差小于一个精度值时,停止搜索,并输出当前的 (mid)。

代码实现

#include <bits/stdc++.h>
using namespace std;

bool canFind(const vector<int>& arr, int n, int k, double avg) {
    vector<double> sum(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        sum[i] = sum[i - 1] + arr[i - 1] - avg;
    }
    double minSum = 0;
    for (int i = k; i <= n; ++i) {
        if (sum[i] - minSum >= 0) return true;
        minSum = min(minSum, sum[i - k + 1]);
    }
    return false;
}

int main() {
    int n, k;
    cin >> n >> k;
    vector<int> arr(n);
    for (int i = 0; i < n; ++i) {
        cin >> arr[i];
    }

    double l = -1e4, r = 1e4, res = 0;
    while (r - l > 1e-7) {  // 精度到达 1e-7
        double mid = (l + r) / 2;
        if (canFind(arr, n, k, mid)) {
            res = mid;
            l = mid;
        } else {
            r = mid;
        }
    }
    cout << fixed << setprecision(6) << res;
    return 0;
}

全部评论

相关推荐

做黑夜里的那道光:两年电赛完赛没必要写,纯扣分
双非本科求职如何逆袭
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务