Maximum Average Pass Ratio

medium heap greedy

Problem

Each class is given as [pass, total]: of its total exam takers, pass will pass. You also have extraStudents brilliant students who are guaranteed to pass whatever class you place them in — assigning one to a class turns its ratio from pass/total into (pass+1)/(total+1).

Place every extra student so that the average pass ratio (the mean of pass/total over all classes) is as large as possible. Answers within 10⁻⁵ of the optimum are accepted.

Inputclasses = [[1,2],[3,5],[2,2]], extraStudents = 2
Output0.78333
Both extras go to the first class (1/2 → 3/4): (3/4 + 3/5 + 2/2) / 3 ≈ 0.78333.

import heapq

def max_average_ratio(classes, extra_students):
    def gain(p, t):
        return (p + 1) / (t + 1) - p / t

    heap = [(-gain(p, t), p, t) for p, t in classes]
    heapq.heapify(heap)
    for _ in range(extra_students):
        g, p, t = heapq.heappop(heap)
        p, t = p + 1, t + 1
        heapq.heappush(heap, (-gain(p, t), p, t))
    return sum(p / t for _, p, t in heap) / len(classes)
function maxAverageRatio(classes, extraStudents) {
  const gain = (p, t) => (p + 1) / (t + 1) - p / t;
  // max-heap of [gain, pass, total] (array kept sorted)
  const heap = classes.map(([p, t]) => [gain(p, t), p, t]);
  heap.sort((a, b) => b[0] - a[0]);
  for (let i = 0; i < extraStudents; i++) {
    const [, p, t] = heap.shift();   // class with best gain
    heap.push([gain(p + 1, t + 1), p + 1, t + 1]);
    heap.sort((a, b) => b[0] - a[0]);
  }
  return heap.reduce((s, [, p, t]) => s + p / t, 0) / classes.length;
}
double maxAverageRatio(int[][] classes, int extraStudents) {
    PriorityQueue<double[]> heap =
        new PriorityQueue<>((a, b) -> Double.compare(b[0], a[0]));
    for (int[] c : classes) {
        double p = c[0], t = c[1];
        heap.offer(new double[]{(p + 1) / (t + 1) - p / t, p, t});
    }
    for (int i = 0; i < extraStudents; i++) {
        double[] top = heap.poll();
        double p = top[1] + 1, t = top[2] + 1;
        heap.offer(new double[]{(p + 1) / (t + 1) - p / t, p, t});
    }
    double sum = 0;
    for (double[] c : heap) sum += c[1] / c[2];
    return sum / classes.length;
}
double maxAverageRatio(vector<vector<int>>& classes, int extraStudents) {
    auto gain = [](double p, double t) {
        return (p + 1) / (t + 1) - p / t;
    };
    priority_queue<tuple<double, double, double>> heap;
    for (auto& c : classes)
        heap.push({gain(c[0], c[1]), c[0], c[1]});
    for (int i = 0; i < extraStudents; i++) {
        auto [g, p, t] = heap.top(); heap.pop();
        heap.push({gain(p + 1, t + 1), p + 1, t + 1});
    }
    double sum = 0;
    while (!heap.empty()) {
        auto [g, p, t] = heap.top(); heap.pop();
        sum += p / t;
    }
    return sum / classes.size();
}
Time: O((n + k) log n) Space: O(n)