[LeetCode-1792.最大平均通过率]

        ps:看力扣官方题解后的笔记

        题解中要求算出最大平均通过率,所有班级的平均通过率公式为:

\frac{\sum \frac{pass_{i}}{total_{i}}}{classes.size}

        在此公式中,班级的数量固定不变,所以所有班级的通过率之和决定平均通过率的大小。

       


        而对于任意一个班级,每增加一个通过学生后,此班级的通过率公式如下

        f(x) = \frac{p+x}{t+x} = 1+\frac{p-t}{x+t}

         其中p为班级原通过人数,t为班级原总数,x为增加额外通过学生的个数

         求一阶导:

        f(x)^{'} = \frac{t-p}{(t+x)^{2}} >=0

 一阶导函数大于等于零,说明随着在一个班级中添加通过人数的增大,该班级的通过率也在增大。

         求二阶导:

        f(x)^{(2)} = \frac{2(p-t)}{(t+x)^{3}} <=0

二阶导函数小于等于零,说明一阶导函数递减,而一阶导函数值是原函数的切线值,也是原函数的变化率在递减。即,每增加一位通过学生,通过率在上升,但每次的通过率上升量在减少。


        回到题目中,我们需要将extra个学生分配到这些班级中去。可以每次分配一个学生,找到变化率最大的那个班级,将该学生放入其中。这样在每次分配一个学生的时候,总通过率率的增幅最大。这样所有学生分配结束后,总通过率的值是最大的。

        当班级i的通过率增幅比班级j的大时,有以下结果:

\frac{pass_{i}+1}{total_{i}+1}-\frac{pass_{i}}{total_{i}}>\frac{pass_{j}+1}{total_{j}+1}-\frac{pass_{j}}{total_{j}}

        化简后结果如下

(total_{i}-pass_{i})*total_{j}*(total_{j}+1)>(total_{j}-pass_{j})*total_{i}*(total_{i}+1)

       


        根据每个班级的增加一个成员后的变化率大小来区分每个班级的优先级。

        定义一个结构体,保存每个班级当前的总数和通过人数。并重写<运算符,来比较增加一个额外通过学生后,此班级的通过率增幅的大小。

 struct Ratio{
        int pass;
        int total;
        bool operator<(const Ratio &other)const{
            return (long long)other.total*(other.total+1)*(total-pass)<
                   (long long)(other.total-other.pass)*total*(total+1);
        }
};

        使用优先级队列,将所有班级都放入到队列中。在此队列中,根据每增加一人通过率增幅从大到小排列。每次取出一个增幅最大的班级,加入一个成员后,计算他下一次的增幅大小,然后插入到优先级队列中合适的位置。

double maxAverageRatio(vector<vector<int>>& classes, int extraStudents) {
        // 将所有的通过滤放入到优先级队列中
        priority_queue<Ratio> queue;
        for(auto &c:classes){
            queue.push({c[0],c[1]});
        }
        for(int i =0;i<extraStudents;++i){
            auto [pass,total] = queue.top();
            queue.pop();// 弹出队头元素
            queue.push({pass+1,total+1});
        }
        double ans = 0;
        while(!queue.empty()){
            auto [pass,total] = queue.top();
            queue.pop();
            ans+=(1.0*pass)/(total);
        }
        return ans/classes.size();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值