Loading problems...
TL;DR: The optimal solution uses a Max-Heap to greedily assign each extra student to the class that yields the highest immediate increase in the pass ratio.
You are given a list of classes, each defined by the number of students passing and the total number of students. You are also given a number of extraStudents. Each extra student is guaranteed to pass and can be assigned to any class. The goal of LeetCode 1792 is to distribute these extra students in a way that maximizes the Maximum Average Pass Ratio across all classes.
Since the number of classes is fixed, maximizing the average pass ratio is mathematically equivalent to maximizing the sum of the pass ratios of all classes.
A naive approach would be to simulate the assignment process one student at a time. For each of the extraStudents, we iterate through the entire list of classes to calculate which class would benefit most from adding one passing student.
The algorithm proceeds as follows:
k times (where k is ).extraStudentsn classes.The time complexity of this approach is , where is the number of extra students and is the number of classes. Given the constraints , the total operations could reach , which will inevitably result in a Time Limit Exceeded (TLE) error. We need a way to find the maximum gain without scanning the entire list every time.
The key intuition is to define the benefit of assigning a student mathematically. We define the profit or gain of adding a student to a specific class as the change in its pass ratio:
Where is the number of passing students and is the total students.
We want to maximize the total sum of ratios. Since the "gain" from adding a student diminishes as the class size grows (diminishing returns), we must pick the class with the highest current for every single extra student we assign.
Instead of scanning all classes to find the max (which is ), we can maintain the classes in a Max-Heap (Priority Queue). The heap orders the classes based on their potential gain. This allows us to retrieve the best candidate in time and update the structure in time after assigning a student.
Visual Description: Imagine a Priority Queue as a tiered structure where the class that would improve the most sits at the very top.

Let's trace the algorithm with classes = [[1,2], [3,5], [2,2]] and extraStudents = 2.
Initialization:
[1,2]: Ratio 0.5. New Ratio if added: . Gain: .[3,5]: Ratio 0.6. New Ratio if added: . Gain: .[2,2]: Ratio 1.0. New Ratio if added: . Gain: .[(0.166, 1, 2), (0.066, 3, 5), (0.0, 2, 2)].Student 1 Assignment:
[1,2] with gain .1 -> 2 pass, 2 -> 3 total.Student 2 Assignment:
[2,3] with gain .2 -> 3 pass, 3 -> 4 total.Final Calculation:
The correctness relies on the "Greedy Choice Property." We want to maximize . Since the total number of students to add is fixed, and the contribution of each addition is independent of others except for the class it modifies, we essentially have a sequence of independent choices.
The function describing the gain, , is strictly decreasing as increases (diminishing returns). Adding a student to a class now does not prevent us from adding another student to the same class later; it simply reduces the value of that subsequent addition. Therefore, choosing the local maximum gain at each step guarantees the global maximum sum of ratios.
cpp#include <vector> #include <queue> #include <tuple> using namespace std; class Solution { public: double maxAverageRatio(vector<vector<int>>& classes, int extraStudents) { // Priority queue to store {gain, pass, total} // Ordered by gain descending priority_queue<pair<double, pair<int, int>>> maxHeap; auto calculateGain = [](int pass, int total) { return (double)(pass + 1) / (total + 1) - (double)pass / total; }; // Initialize heap for (const auto& c : classes) { int pass = c[0]; int total = c[1]; maxHeap.push({calculateGain(pass, total), {pass, total}}); } // Distribute extra students while (extraStudents > 0) { auto top = maxHeap.top(); maxHeap.pop(); double currentGain = top.first; int pass = top.second.first; int total = top.second.second; // Update the class pass++; total++; extraStudents--; // Push back with new gain maxHeap.push({calculateGain(pass, total), {pass, total}}); } // Calculate final average double totalRatio = 0.0; while (!maxHeap.empty()) { auto top = maxHeap.top(); maxHeap.pop(); totalRatio += (double)top.second.first / top.second.second; } return totalRatio / classes.size(); } };
javaimport java.util.PriorityQueue; class Solution { public double maxAverageRatio(int[][] classes, int extraStudents) { // PriorityQueue stores arrays: [gain, pass, total] // We use a custom comparator to simulate a Max-Heap based on gain PriorityQueue<double[]> maxHeap = new PriorityQueue<>((a, b) -> Double.compare(b[0], a[0])); for (int[] c : classes) { double pass = c[0]; double total = c[1]; double gain = (pass + 1) / (total + 1) - (pass / total); maxHeap.offer(new double[]{gain, pass, total}); } while (extraStudents > 0) { double[] top = maxHeap.poll(); double pass = top[1]; double total = top[2]; // Add student pass++; total++; extraStudents--; // Recalculate gain and push back double newGain = (pass + 1) / (total + 1) - (pass / total); maxHeap.offer(new double[]{newGain, pass, total}); } double totalRatio = 0.0; while (!maxHeap.isEmpty()) { double[] top = maxHeap.poll(); totalRatio += top[1] / top[2]; } return totalRatio / classes.length; } }
pythonimport heapq class Solution: def maxAverageRatio(self, classes: list[list[int]], extraStudents: int) -> float: # Helper to calculate gain def calculate_gain(p, t): return (p + 1) / (t + 1) - p / t # Python's heapq is a min-heap. We store negative gain to simulate max-heap. # Structure: (-gain, pass, total) heap = [] for p, t in classes: gain = calculate_gain(p, t) heapq.heappush(heap, (-gain, p, t)) # Assign students for _ in range(extraStudents): neg_gain, p, t = heapq.heappop(heap) # Update class p += 1 t += 1 # Recalculate and push new_gain = calculate_gain(p, t) heapq.heappush(heap, (-new_gain, p, t)) # Calculate final average total_ratio = 0 for _, p, t in heap: total_ratio += p / t return total_ratio / len(classes)
Time Complexity:
extraStudents).Space Complexity:
The Heap - Scheduling / Minimum Cost pattern is highly versatile for interview questions. It applies whenever you need to dynamically fetch the "best" current option while modifying the data set.
The naive solution iterates through the entire list of classes for every extra student. If you have classes and extra students, this results in operations, far exceeding the typical limit for a 1-second timeout. The Heap pattern reduces the inner loop to .
[(0.0833, 2, 3), (0.066, 3, 5), (0.0, 2, 2)].[(0.066, 3, 5), (0.05, 3, 4), (0.0, 2, 2)].Mistake: Assuming the class with the lowest pass ratio (e.g., 1/100 vs 1/2) will always provide the biggest boost.
Reasoning: The "gain" depends on the magnitude of the numbers, not just the ratio. Adding 1 student to 1/2 (0.5) makes it 2/3 (0.66), a gain of ~0.16. Adding 1 student to 1/100 (0.01) makes it 2/101 (0.019), a gain of only ~0.009.
Consequence: Your greedy choice will be suboptimal, leading to an incorrect answer.
Mistake: Using integer division (e.g., pass / total in C++ or Java) results in 0 or 1, losing all precision.
Reasoning: Pass ratios are fractional values between 0 and 1. Integer arithmetic truncates these decimals.
Consequence: The logic will treat different ratios as identical (likely 0), breaking the heap ordering and final calculation.
Mistake: Calculating gains once, sorting, and dumping all students into the top class until it's no longer the best. Reasoning: The gain is dynamic. As soon as you add one student to a class, its denominator increases, significantly reducing the gain from the next student added to that same class. The "best" class changes constantly. Consequence: You fail to re-evaluate the greedy choice, leading to a suboptimal distribution.