leetcode
leetcode 1601 ~ 1650
最大平均通过率

最大平均通过率

难度:

标签:

题目描述

代码结果

运行时间: 1059 ms, 内存: 51.5 MB


/*
题目思路:
1. 使用Java Stream处理数据,计算每个班级的初始通过率。
2. 使用PriorityQueue存储通过率增量并进行排序。
3. 通过流操作分配额外学生并计算最大平均通过率。
*/
import java.util.*;
import java.util.stream.*;

public class MaxAveragePassRateStream {
    public double maxAverageRatio(int[][] classes, int extraStudents) {
        PriorityQueue<double[]> maxHeap = new PriorityQueue<>((a, b) -> Double.compare(b[0], a[0]));
        // 初始增量计算
        Arrays.stream(classes).forEach(cls -> {
            int pass = cls[0];
            int total = cls[1];
            double increment = (double)(pass + 1) / (total + 1) - (double)pass / total;
            maxHeap.offer(new double[]{increment, pass, total});
        });
        // 分配额外学生
        IntStream.range(0, extraStudents).forEach(i -> {
            double[] top = maxHeap.poll();
            int pass = (int) top[1];
            int total = (int) top[2];
            pass++;
            total++;
            double increment = (double)(pass + 1) / (total + 1) - (double)pass / total;
            maxHeap.offer(new double[]{increment, pass, total});
        });
        // 计算最终平均通过率
        return maxHeap.stream().mapToDouble(cls -> cls[1] / cls[2]).average().orElse(0.0);
    }
}

解释

方法:

这道题目使用贪心算法和优先队列(堆)来解决。首先,我们计算每个班级添加一个额外学生后的通过率增加量,即(a / b - (a + 1) / (b + 1)),其中a是通过的学生数,b是班级总人数。我们将这个增加量、通过的学生数和总人数作为一个元组放入一个最小堆中。然后,我们逐个将额外的学生分配给能使通过率增加量最大的班级,即每次从堆中弹出增加量最大的元组,更新该班级的通过人数和总人数,重新计算增加量,然后将更新后的元组再次推入堆中。重复这个过程,直到所有额外的学生都被分配完。最后,我们计算所有班级的平均通过率并返回。

时间复杂度:

O(n + k log n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么选择使用优先队列(堆)来管理班级的通过率增加量,而不是其他数据结构如数组或链表?
优先队列(或堆)在此算法中被使用是因为它能够提供高效的插入和删除最大元素的操作。在每次分配学生时,我们需要寻找当前哪个班级的通过率增加量最大,这是一个典型的最大元素查询操作。使用最大堆可以在O(log n)的时间复杂度内完成这些操作,而如果使用数组或链表,则查找最大元素需要O(n)的时间复杂度。因此,使用堆结构可以显著提高算法的效率。
🦆
在计算通过率增加量时,为什么将增加量定义为`a / b - (a + 1) / (b + 1)`,这种定义能准确反映添加学生后的变化吗?
此定义`a / b - (a + 1) / (b + 1)`准确地反映了在给定班级中添加一个学生后通过率的变化量。这是因为`a / b`是班级当前的通过率,而`(a + 1) / (b + 1)`是在添加一个学生后的新通过率。这个差值表示了通过率的增减,是评估哪个班级通过添加一个学生可以获得最大通过率提升的关键依据。
🦆
在题解中提到的`最小堆`实际上应该是最大堆吧,因为需要优先处理增加量最大的班级,这里是不是有误?
确实,描述中存在误导。题解中应该使用的是最大堆,因为我们需要优先处理那些使通过率增加量最大的班级。在代码实现中,通常可以通过将增加量取负值的方式使用最小堆来模拟最大堆的行为,从而实现相同的效果。
🦆
如果在分配完所有额外学生后,某些班级的通过率实际上减少了怎么办?这种情况在当前算法中有考虑吗?
在这个算法中,每次都是选择可以获得最大正增长的班级来添加学生,所以在分配过程中不会出现通过率减少的情况。每次操作都确保了选择的是增加量最大(即正增长)的班级。算法的目的是最大化整体的平均通过率,而不是单个班级的通过率。因此,虽然单个班级的通过率可能较低,总体平均通过率是在提升的。

相关问题