<p>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 <code>classes</code>, where <code>classes[i] = [pass<sub>i</sub>, total<sub>i</sub>]</code>. You know beforehand that in the <code>i<sup>th</sup></code> class, there are <code>total<sub>i</sub></code> total students, but only <code>pass<sub>i</sub></code> number of students will pass the exam.</p>

<p>You are also given an integer <code>extraStudents</code>. There are another <code>extraStudents</code> brilliant students that are <strong>guaranteed</strong> to pass the exam of any class they are assigned to. You want to assign each of the <code>extraStudents</code> students to a class in a way that <strong>maximizes</strong> the <strong>average</strong> pass ratio across <strong>all</strong> the classes.</p>

<p>The <strong>pass ratio</strong> 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 <strong>average pass ratio</strong> is the sum of pass ratios of all the classes divided by the number of the classes.</p>

<p>Return <em>the <strong>maximum</strong> possible average pass ratio after assigning the </em><code>extraStudents</code><em> students. </em>Answers within <code>10<sup>-5</sup></code> of the actual answer will be accepted.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> classes = [[1,2],[3,5],[2,2]], <code>extraStudents</code> = 2
<strong>Output:</strong> 0.78333
<strong>Explanation:</strong> 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.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> classes = [[2,4],[3,9],[4,5],[2,10]], <code>extraStudents</code> = 4
<strong>Output:</strong> 0.53485
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= classes.length &lt;= 10<sup>5</sup></code></li>
	<li><code>classes[i].length == 2</code></li>
	<li><code>1 &lt;= pass<sub>i</sub> &lt;= total<sub>i</sub> &lt;= 10<sup>5</sup></code></li>
	<li><code>1 &lt;= extraStudents &lt;= 10<sup>5</sup></code></li>
</ul>

class Solution:
    def maxAverageRatio(self, classes: List[List[int]], extraStudents: int) -> float:
        # 1. Non Trivial Example
        # 2. Intuition - data structure or algorithm. (based on experience and skill).
	# 1-2 may be interchangeable steps.
        # 3. Execution of intuition on example.
        # 4. Invariant - Heart of our algorithm, that proves algorithm's correctness.
        # 5. Writing algorithm in a freestyle English step by step manner.
	# 4-5 may also be interchangeable .
        # 6. Execution and debugging the english algorithm on example.
        # 7. Writing down the code - clean code (requires a little skill)
        # 8. Execution and debugging the code on example.

        # 1. non trivial example: [[1,2],[3,5],[2,2]] 
        # 2. intuition: 
        # input -> filter all 100% -> sort by size of class -> 
        # [[1,2], [50,100]] -> 50% 50% -> 66% 50% -> 75% 50.5%
        # [[1,3], [1,4]] -> 50%->66% 33%->50%
        # [1,3], [3,7]
        # 16%. 15%

        # 4. invariant: increasing the students in smallest class will have the biggest impact on each iteration - Greedy algo
        # 5.
        # based on input we count the num of fully successful aside
        # we filter out successful classes
        # we init a max heap and we add all tuples of students by comperator of improvement but also the smallest class
        # we iterate extraStudents time and we pop the smallest class and add a +1 to each side
        # we convert the heap to a list
        # we return sum all the averages + successful count
        import heapq
        non_perfect_classes = [class_score for class_score in classes if class_score[0] != class_score[1]]
        perfect_classes = len(classes) - len(non_perfect_classes)
        heap = []
        for class_score in non_perfect_classes:
            improvement = (class_score[0] + 1) / (class_score[1] + 1) - class_score[0] / class_score[1]
            heapq.heappush(heap, ((-1) * improvement, tuple(class_score)))
        if len(heap) > 0:
            for i in range(extraStudents):
                cur = heapq.heappop(heap)
                class_score = cur[1]
                class_score_updated = (class_score[0] + 1, class_score[1] + 1)
                improvement = (class_score_updated[0] + 1) / (class_score_updated[1] + 1) - class_score_updated[0] / class_score_updated[1]
                heapq.heappush(heap, ((-1) * improvement, class_score_updated))
        scores = [cur[1][0] / cur[1][1] for cur in heap] 
        return (sum(scores) + perfect_classes) / (len(scores) + perfect_classes)

        # 0.66 -> 0.75, 0.6 -> 0.66




1809