`[特殊字符]LeetCode每日一题 1792. 最大平均通过率(打卡第一天)`
🐋LeetCode每日一题 1792. 最大平均通过率(打卡第一天)
一所学校里有一些班级,每个班级里有一些学生,现在每个班都会进行一场期末考试。给你一个二维数组 classes ,其中 classes[i] = [passi, totali] ,表示你提前知道了第 i 个班级总共有 totali 个学生,其中只有 passi 个学生可以通过考试。
给你一个整数 extraStudents ,表示额外有 extraStudents 个聪明的学生,他们 一定 能通过任何班级的期末考。你需要给这 extraStudents 个学生每人都安排一个班级,使得 所有 班级的 平均 通过率 最大 。
一个班级的 通过率 等于这个班级通过考试的学生人数除以这个班级的总人数。平均通过率 是所有班级的通过率之和除以班级数目。
请你返回在安排这 extraStudents 个学生去对应班级后的 最大 平均通过率。与标准答案误差范围在 10-5 以内的结果都会视为正确结果。
示例 1:
输入:classes = [[1,2],[3,5],[2,2]], extraStudents = 2
输出:0.78333
解释:你可以将额外的两个学生都安排到第一个班级,平均通过率为 (3/4 + 3/5 + 2/2) / 3 = 0.78333 。
示例 2:
输入:classes = [[2,4],[3,9],[4,5],[2,10]], extraStudents = 4
输出:0.53485
提示:
1 <= classes.length <= 105
classes[i].length == 2
1 <= passi <= totali <= 105
1 <= extraStudents <= 105
题解:
在确定天才学生的放置时,应该优先选择提升潜力更大的位置进行放置,这样才可保证全局最优,而不是仅仅由放置后的班级通过率决定,代码中法一即出现此误区,认为局部最优为全局最优。
代码:
// 误区1: 误以为局部最优即为全局最优 即应该选择提升潜力更大 而不是仅仅值大
// 误区2: (double)(classes[i][0] / classes[i][1]) 这样会导致先进行整型运算再转换为double 会损失精度
// 即应该先转为double再进行除法运算
// 误区3: 应利用优先队列实现堆 而非每次进行重新排序
class Solution {public double maxAverageRatio(int[][] classes, int extraStudents) {// 通过率达1的必然不会加人 或理解为加不加对通过率没有影响 依然为1// 利用堆排序思想 先对classes预处理 对其每一个都进行分母分子+1模拟 // 第一次故选择建好大根堆中堆顶元素// 之后仅对选中元素进行更新 更新后继续放入堆中 重复上述选择堆顶操作int al_ok = 0;for(int i=0;i<classes.length;i++){if(classes[i][0] == classes[i][1]){al_ok++;}}Exm[] exm = new Exm[classes.length-al_ok];// 100%的记得剔除int index = 0;for(int i=0;i<classes.length;i++){if(classes[i][0] == classes[i][1])continue;Exm e = new Exm();e.id = i;e.val = (double)classes[i][0] / classes[i][1];e.ok = classes[i][0];e.count = classes[i][1];e.later_val = (double)(classes[i][0] + 1) / (classes[i][1] + 1);exm[index++] = e;}Arrays.sort(exm, (o1, o2) -> Double.compare(o2.later_val, o1.later_val));for(int i=0;i<extraStudents;i++){exm[0].val = exm[0].later_val;exm[0].ok++;exm[0].count++;exm[0].later_val = (double)(exm[0].ok + 1) / (exm[0].count + 1);Arrays.sort(exm, (o1, o2) -> Double.compare(o2.later_val, o1.later_val));}double res = al_ok;for(int i=0;i<classes.length-al_ok;i++){res += exm[i].val;}return (double) res / classes.length;}
}
class Exm{int id; // 班级编号int ok; // 此时可通过的人数int count; // 此时总人数double val; // 此时通过率double later_val; // +1处理后通过率
}// 修改后
// 即无需单独建类 使用纯double数组即可实现 因题目仅仅需要进行数值计算
class Solution {public double maxAverageRatio(int[][] classes, int extraStudents) {// 基于提升潜力进行排序 此时即可不用特别筛选出100%PriorityQueue<Double[]> heap = new PriorityQueue<>((o1,o2) -> {double p1 = ((o1[0] + 1) / (o1[1] + 1)) - (o1[0] / o1[1]);double p2 = ((o2[0] + 1) / (o2[1] + 1)) - (o2[0] / o2[1]);return Double.compare(p2,p1);});int len = classes.length;for(int i=0;i<len;i++){Double[] tmp = new Double[2];tmp[0] = classes[i][0] + 0.0;tmp[1] = classes[i][1] + 0.0;heap.offer(tmp);}for(int i=0;i<extraStudents;i++){Double[] tmp = heap.poll();tmp[0]++;tmp[1]++;heap.offer(tmp);}double res = 0;while(!heap.isEmpty()){Double[] tmp = heap.poll();double r1 = tmp[0];double r2 = tmp[1];res += (r1/r2);}return res / len;}
}
结果:
题外话:
这里看到了一个很唯美的题解,粘贴在这里供大家瞻仰!