平均值(水题???)

时间:2024-11-19 07:32:12

 今天刷题时发现了一道十分简单的题。大家仔细看看题目。


 题目

5. K11937 平均值

题目描述

在演讲比赛中,当参赛者完成演讲时,评委会对他的表演进行评分。工作人员会去掉一个最高分,一个最低分,然后计算其余的平均值作为参赛者的最终成绩。

现在给定n个正整数,去掉最大的n1个数和最小的n2数,然后计算其余的平均值

输入格式

输入包含多组测试数据,每组数据包含两个两行,第一行是三个整数n1 n2 n(1≤n1,n2≤10,n1+n2≤n≤5*10^6)

第二行是n个整数,每个整数的范围是1到10^8

当输入为一行“0 0 0”表示输入结束

输出格式

对于每组测试数据,输出平均值,结果保留6位小数

输入输出样例
输入样例1:复制

1 2 5

1 2 3 4 5

4 2 10

2121187 902 485 531 843 582 652 926 220 155

0 0 0

输出样例1:复制

3.500000

562.500000

【耗时限制】4000ms 【内存限制】10MB

注意 

 不知道大家有没有发现啊,这一题的【耗时限制】【内存限制】有些特别。

【耗时限制】4000ms 【内存限制】10MB

4000m很大,但10MB......

我们来算算


10MB=10240KB≈10^7bits 一个int类型的变量占4个字节

1≤n≤5*10^6    数组要开这么大:5000000

10^7/4=2.5*10^6 只有2500000个int,况且程序运行还要空间

不说算法,开个数组就爆了好吗!!!


这咋写啊!!!

推算一下

 先不说超空间,来看看基本写法:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n1,n2,n,a[5000010];
int main()
{
    while(cin>>n1>>n2>>n&&n1+n2+n>0){
        for(LL i=1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        LL sum=0;
        for(LL i=n2+1;i<=n-n1;i++) sum+=a[i];
        printf("%.6lf\n",sum*1.0/(n-n1-n2));
    }
    return 0;
}

如果你仍然啥也看不出来

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n1,n2,n,a[5000010];
int main()
{
    while(cin>>n1>>n2>>n&&n1+n2+n>0){
        LL sum=0;
        for(LL i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            sum+=a[i];
        }
        sort(a+1,a+n+1);
        for(LL i=1;i<=n2;i++) sum-=a[i];
        for(LL i=n-n1+1;i<=n;i++) sum-=a[i];
        printf("%.6lf\n",sum*1.0/(n-n1-n2));
    }
    return 0;
}

诶,sum+=a[i]不需要排序就能直接加,也就是说,可以是这样:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n1,n2,n,a;
int main()
{
    while(cin>>n1>>n2>>n&&n1+n2+n>0){
        LL sum=0;
        for(LL i=1;i<=n;i++){
            scanf("%lld",&a);
            sum+=a;
        }
        //去掉最大的n1个数
        //去掉最小的n2个数
        printf("%.6lf\n",sum*1.0/(n-n1-n2));
    }
    return 0;
}

现在的问题是怎么“去掉最大的n1个数”“去掉最小的n2个数”


开始正片Action

 意料之外,情理之中

怎样“去掉最大的n1个数”“去掉最小的n2个数”

答案是优先队列

先来回忆一下优先队列:

->priority_queue是c++提供的一个优先队列的实现,使用二叉堆实现。<-

->priority_queue默认是大根堆, 堆顶元素即为序列中的最大<-

->priority_queue默认为大根堆,可以较方便的获取最大值,想要获取最小值时,可以将默认 的大根堆修改为小根堆。<-

->二叉堆适用于在一个动态变化的序列中查找极大值或极小值<-

->二叉堆一般应用在贪心和动态规划类问题中,用于阶段性最优值的获取。<-

欸,这和“最大的n1个数”“最小的n2个数”有什么关系???

其实,优先队列不仅能查找极大值或极小值,还可以维护一群最小值/最大的。

先翻到文章末尾来做个选择

---------------------------------------------------------如何实现?---------------------------------------------------------

来一个,更新一次

这是一个容器

————————

|           点赞          |

|          关注           |

————————

来了一个元素就放进去

如果元素数量比n1大了

就把最小的踢了

这样就实现了维护一群最大的

既然要“把最小的踢了”,就该用小根堆

维护一群最小值,就该用大根堆


理一理:

        小根堆->(维护一群最大的/极小值)

        大根堆->(维护一群最小值/极大值)

你学废了吗?

这样这题就能这样写:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n1,n2,n,a;
priority_queue<LL> pq1;
priority_queue<LL,vector<LL>,greater<LL> > pq2;
int main()
{
    while(cin>>n1>>n2>>n&&n1+n2+n>0){
        LL sum=0;
        for(LL i=1;i<=n;i++){
            scanf("%lld",&a);
            pq1.push(a);
            pq2.push(a);
            sum+=a;
            if(pq1.size()>n2) pq1.pop();
            if(pq2.size()>n1) pq2.pop();
        }
        //去掉最大的n1个数
        while(!pq1.empty()) sum-=pq1.top(),pq1.pop();
        //去掉最小的n2个数
        while(!pq2.empty()) sum-=pq2.top(),pq2.pop();
        printf("%.6lf\n",sum*1.0/(n-n1-n2));
    }
    return 0;
}

评测结果

 

 大家可以试试“K阶恒星系”,也是这样子滴!!!