题目是LeetCode第180场周赛的第四题,链接:最大的团队表现值。具体描述为:公司有编号为 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:
输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 3
输出:68
解释:
此示例与第一个示例相同,除了 k = 3 。我们可以选择工程师 1 ,工程师 2 和工程师 5 得到最大的团队表现值。表现值为 performance = (2 + 10 + 5) * min(5, 4, 7) = 68 。
示例3:
输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 4
输出:72
这是个hard难度的题目,一开始也是以为就是个动态规划的公式,陷了进去愣是找不到递推公式。后来比赛结束后才在评论区看到这压根就不是动态规划的题目,而是贪心算法。。。
因为效率取决于k个人中的最小效率,所以直接对效率降序排序(此时对应的速度也会被排序),那么可以看到,只要指定最小效率为降序数组的第i个效率,那么当前最大团队表现值就一定是前面的min(k, i)个最大speed之和乘以这个最小效率,也就是说,只需遍历一遍降序数组就可以得到结果了。具体过程为:
- 对于一个二维数组(speed, efficiency)按efficiency降序排序得到s
- 初始化一个大小为k的最小堆topk用以维护k个最大speed
- 对于前k个efficiency,直接将speed加入最小堆,同时统计全部speed之和totalSpeed,更新最大团队表现值
- 对于k个之后的efficiency,只有当对应的speed小于k个speed中的最小值(即堆顶元素)时才有必要继续更新totalSpeed以及最大团队表现值,否则可以跳过
因为排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),遍历过程则只需要 O ( n l o g k ) O(nlogk) O(nlogk)( l o g k logk logk为将数据加入最小堆所需时间),所以总的时间复杂度为 O ( n l o g n ) + O ( n l o g k ) O(nlogn)+O(nlogk) O(nlogn)+O(nlogk),空间复杂度为 O ( n ) O(n) O(n)(存储排序后数据)。
JAVA版代码如下:
class Solution {
public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
int[][] speedAndEff = new int[n][2];
for (int i = 0; i < n; ++i) {
speedAndEff[i][0] = speed[i];
speedAndEff[i][1] = efficiency[i];
}
Arrays.sort(speedAndEff, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o2[1] - o1[1];
}
});
Queue topk = new PriorityQueue<Integer>(k);
long maxResult = 0, totalSpeed = 0;
for (int i = 0; i < k; ++i) {
topk.offer(speedAndEff[i][0]);
totalSpeed += speedAndEff[i][0];
maxResult = Math.max(maxResult, totalSpeed * speedAndEff[i][1]);
}
for (int i = k; i < n; ++i) {
if (speedAndEff[i][0] > (int)topk.peek()) {
totalSpeed = totalSpeed - (int)topk.poll() + speedAndEff[i][0];
topk.offer(speedAndEff[i][0]);
maxResult = Math.max(maxResult, totalSpeed * speedAndEff[i][1]);
}
}
return (int)(maxResult % 1000000007);
}
}
提交结果如下:
Python版代码如下:
class Solution:
def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int:
speedAndEff = list(zip(speed, efficiency)) #组成(speed, efficiency)
speedAndEff.sort(key=lambda x : x[1], reverse=True) #按efficiency降序排序
import heapq
topk = []
maxResult, totalSpeed = 0, 0
for i in range(k): #前k个全部加入最小堆中,同时计算最大表现值(可以小于k个人)
heapq.heappush(topk, speedAndEff[i][0])
totalSpeed += speedAndEff[i][0]
maxResult = max(maxResult, totalSpeed * speedAndEff[i][1])
for i in range(k, n): #对于k个之后的,只有speed大于当前最小speed的才有必要继续计算
if speedAndEff[i][0] > topk[0]:
totalSpeed = totalSpeed - topk[0] + speedAndEff[i][0]
heapq.heapreplace(topk, speedAndEff[i][0])
maxResult = max(maxResult, totalSpeed * speedAndEff[i][1])
return maxResult % 1000000007
提交结果如下: