引入
在本周周赛中,有这么一道题🔗:
公司有编号为 1 到 n 的 n 个工程师,给你两个数组 speed 和 efficiency ,其中 speed[i] 和 efficiency[i] 分别代表第 i 位工程师的速度和效率。请你返回由最多 k 个工程师组成的 最大团队表现值 ,由于答案可能很大,请你返回结果对 10^9 + 7 取余后的结果。
团队表现值 的定义为:一个团队中「所有工程师速度的和」乘以他们「效率值中的最小值」。
示例 1:
输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 2
输出:60
解释:
我们选择工程师 2(speed=10 且 efficiency=4)和工程师 5(speed=5 且 efficiency=7)。他们的团队表现值为 performance = (10 + 5) * min(4, 7) = 60 。
示例 2:
首先看到这道题的时候,很容易明白这道题与0,1背包问题有相似之处,不同之处在于是乘以效率的最小值,而不是分别乘以效率。
所以直接想到使用排序,将efficiency从小到大的排序,依次从小到大选择,不过这样做的难点在于选取speed我们很容易想到是从大到小的选取,所以如何排除小efficiency大speed是值得思考的问题。
在本题题解中, 我们使用了另外一种思路,将efficiency从大到小的排序,为此,我们用到了小顶堆,Java里我们使用PriorityQueue这个数据结构。
另外用到了优先队列的题在leetcode上还有很多,比如合并K个排序链表、数组中的第K个最大元素、滑动窗口最大值、丑数II等等,用了优先队列(堆)后问题就迎刃而解了。
解法
Java中实现了自己的优先队列java.util.PriorityQueue,Java中内置的为小顶堆,也就是说最小的值是优先出队的。
利用这个特点,我们有如下的代码:
public class Solution {
public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
List<Employee> employees=new ArrayList<>();
for(int i=0;i<n;i++){
employees.add(new Employee(speed[i],efficiency[i]));
}
Collections.sort(employees,(o1, o2) -> o2.effciency-o1.effciency);
PriorityQueue<Integer> queue=new PriorityQueue<>();
long res=0,sum=0;
for(int i=0;i<n;i++){
int sp=employees.get(i).speed;
int ef=employees.get(i).effciency;
queue.add(sp);
sum+=sp;
if (queue.size()>k){
sum-=queue.poll();
}
res=Math.max(res,sum*ef);
}
return (int)(res % ((int)1e9 + 7));
}
class Employee{
int speed;
int effciency;
public Employee(int speed, int effciency) {
this.speed = speed;
this.effciency = effciency;
}
}
}
首先,我们将speed和efficiency进行排序,当然两者要同时排序,可以使用二维数组,也可以使用类,当然也可以自己实现排序算法。
这里我们选择使用一个Employee类来表示这个二元组,并对efficiency进行从大到小的排序。
然后依次按照efficiency的大小入优先队列(也可以说是放入堆中),放入队列中的元素不是efficiency本身,而是speed,这样出队的时候,我们可以把最小的speed排除出去,这样保证我们堆中的speed是最大的。
这样做是不是会有这样的隐患呢?我们试想一下,上面的操作有下面的两种结果。
- 进入最小的speed。
- 进入非最小的speed。
如果是进入的最小的speed,那么这个speed会被马上排除出去,按道理来说speed被排除出去,此时乘以的efficiency不是应该存在的,因为speed没有被采纳,但是由于我们取得是最大值Math.max,efficiency是递减的,结果必然会比之前的值res要小,所以不会被采纳。
如果是进入的非最小speed,那么有一个更高efficiency的小speed被排除出去,由于我们这步没有依赖其他efficiency,所以结果不受影响。
这样,使用一个小顶堆的优先队列就能完成题解。