📌  相关文章
📜  具有给定平均值的不同元素序列中的最大可能数量(1)

📅  最后修改于: 2023-12-03 15:07:06.786000             🧑  作者: Mango

题目描述

给定一个包含 $n$ 个正整数的数组 $a$,以及一个 $k$,你需要在其中选择 $k$ 个数,使得这 $k$ 个数的和的平均值尽可能大。请输出这个最大可能的平均值,保留 $2$ 位小数。

示例输入输出

输入:

6 3
1 2 3 4 5 6

输出:

4.50
思路说明
方法一:贪心法

题目中要求选择 $k$ 个数的和的平均值尽可能大,即要尽可能地选取较大的数。所以,这里可以采用贪心的思想:每次选择数组中最大的 $k$ 个数,计算这 $k$ 个数的和的平均值,然后再逐步缩小选择范围,直到找到最大平均值。具体实现细节见下方代码。

方法二:二分答案法

另一种解法是使用二分答案法。根据二分答案的思路,首先可以确定一个平均值 $mid$,然后使用类似于滑动窗口的方法,判断是否能找到 $k$ 个数,使得这 $k$ 个数的和的平均值大于等于 $mid$。逐步缩小平均值的范围,最终得到最大的平均值。具体实现细节见下方代码。

代码实现

方法一:贪心法的代码实现

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int a[maxn];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1,greater<int>());//对数组进行降序排列
    double ans=-1.0;
    for(int i=k;i<=n;i++)
    {
        double sum=0;
        for(int j=1;j<=i;j++) sum+=a[j];
        ans=max(ans,sum/i);//更新答案
    }
    printf("%.2lf\n",ans);
    return 0;
}

方法二:二分答案法的代码实现

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int a[maxn];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    double l=0,r=100010;//最小平均值为0,最大平均值为100010
    while(r-l>=1e-5)//精度控制
    {
        double mid=(l+r)/2;
        double sum=0,mn=0;
        bool flag=false;
        for(int i=1;i<=n;i++)
        {
            sum+=a[i]-mid;
            if(i>=k)//判断是否达到了k个数
            {
                mn=min(mn,sum);
                if(sum-mn>=0)//说明找到了k个数,平均值大于等于mid
                {
                    flag=true;
                    break;
                }
            }
        }
        if(flag) l=mid;//如果找到了,说明平均值可以更大
        else r=mid;//否则需要缩小平均值
    }
    printf("%.2lf\n",l);
    return 0;
}
时间复杂度

方法一:贪心法的时间复杂度为 $\mathcal{O}(n\log n+k^2)$,其中 $\mathcal{O}(n\log n)$ 是排序的时间复杂度,$\mathcal{O}(k^2)$ 是求和的复杂度。

方法二:二分答案法的时间复杂度为 $\mathcal{O}(n\log m)$,其中 $m$ 表示平均值的范围,本题中 $m$ 最大为 $10^5$。所以,本方法也是完全可行的。