There is a school that has classes of students and each class will be having a final exam. You are given a 2D integer array classes
, where classes[i] = [passi, totali]
. You know beforehand that in the ith
class, there are totali
total students, but only passi
number of students will pass the exam.
You are also given an integer extraStudents
. There are another extraStudents
brilliant students that are guaranteed to pass the exam of any class they are assigned to. You want to assign each of the extraStudents
students to a class in a way that maximizes the average pass ratio across all the classes.
The pass ratio of a class is equal to the number of students of the class that will pass the exam divided by the total number of students of the class. The average pass ratio is the sum of pass ratios of all the classes divided by the number of the classes.
Return the maximum possible average pass ratio after assigning the extraStudents
students. Answers within 10-5
of the actual answer will be accepted.
Example 1:
Input: classes = [[1,2],[3,5],[2,2]], extraStudents
= 2
Output: 0.78333
Explanation: You can assign the two extra students to the first class. The average pass ratio will be equal to (3/4 + 3/5 + 2/2) / 3 = 0.78333.
Example 2:
Input: classes = [[2,4],[3,9],[4,5],[2,10]], extraStudents
= 4
Output: 0.53485
Constraints:
1 <= classes.length <= 105
classes[i].length == 2
1 <= passi <= totali <= 105
1 <= extraStudents <= 105
This problem asks to maximize the average pass ratio of classes by strategically assigning extra students. The key insight is that assigning a student to a class with a smaller class size yields a larger increase in the pass ratio.
The optimal solution employs a greedy approach using a priority queue (max-heap). The strategy is to iteratively assign the extra students to the classes that offer the largest increase in pass ratio per student. We don't need to explore all possible combinations of student assignments because of the greedy nature of this algorithm.
Calculate the potential improvement: For each class [pass_i, total_i]
, calculate the increase in the pass ratio if one extra student is added: (pass_i + 1) / (total_i + 1) - pass_i / total_i
. This represents the potential improvement achieved by assigning a student to that specific class.
Priority Queue: Use a max-heap (priority queue) to store the classes, prioritized by their potential improvement calculated in the previous step. The classes with higher potential improvement will have higher priority.
Iterative Assignment: Repeatedly extract the class with the highest potential improvement from the heap (the class that will see the most significant boost in pass ratio). Assign an extra student to this class by incrementing both pass_i
and total_i
. Then, recalculate the new potential improvement for this updated class and reinsert it into the heap. Repeat this process for all extraStudents
.
Calculate Average Pass Ratio: After assigning all extra students, iterate through the updated classes and compute the average pass ratio.
Time Complexity: The dominant operations are heap operations (insertion and extraction). Each of the extraStudents
iterations involves a heappop
and heappush
, which are O(log n) operations, where n is the number of classes. Therefore, the overall time complexity is O(extraStudents * log n). In the worst case, this could be O(n log n) if extraStudents
is approximately equal to n.
Space Complexity: The priority queue stores at most n elements (the number of classes), so the space complexity is O(n).
import heapq
def maxAverageRatio(classes, extraStudents):
heap = []
for pass_i, total_i in classes:
improvement = (pass_i + 1) / (total_i + 1) - pass_i / total_i
heapq.heappush(heap, (-improvement, pass_i, total_i)) # Use negative for max-heap
for _ in range(extraStudents):
improvement, pass_i, total_i = heapq.heappop(heap)
pass_i += 1
total_i += 1
new_improvement = (pass_i + 1) / (total_i + 1) - pass_i / total_i
heapq.heappush(heap, (-new_improvement, pass_i, total_i))
total_ratio = 0
for _, pass_i, total_i in heap:
total_ratio += pass_i / total_i
return total_ratio / len(classes)
The code in other languages (Java, C++, Go) follows the same algorithmic structure, only differing in the syntax and specific data structures used for the priority queue. The core idea of the greedy approach and the use of a max-heap remain consistent.