2462. 雇佣 K 位工人的总代价
给你一个下标从 0 开始的整数数组 costs ,其中 costs[i] 是雇佣第 i 位工人的代价。
同时给你两个整数 k 和 candidates 。我们想根据以下规则恰好雇佣 k 位工人:
总共进行 k 轮雇佣,且每一轮恰好雇佣一位工人。
在每一轮雇佣中,从最前面 candidates 和最后面 candidates 人中选出代价最小的一位工人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
比方说,costs = [3,2,7,7,1,2] 且 candidates = 2 ,第一轮雇佣中,我们选择第 4 位工人,因为他的代价最小 [3,2,7,7,1,2] 。
第二轮雇佣,我们选择第 1 位工人,因为他们的代价与第 4 位工人一样都是最小代价,而且下标更小,[3,2,7,7,2] 。注意每一轮雇佣后,剩余工人的下标可能会发生变化。
如果剩余员工数目不足 candidates 人,那么下一轮雇佣他们中代价最小的一人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
一位工人只能被选择一次。
返回雇佣恰好 k 位工人的总代价。
采用最小堆
class Solution {
public long totalCost(int[] costs, int k, int candidates) {
long ans = 0;
int n = costs.length;
if(2*candidates+k>n) {
Arrays.sort(costs);
for(int i=0; i<k; i++) ans += costs[i];
return ans;
}
Queue<Integer> pre = new PriorityQueue();
Queue<Integer> aft = new PriorityQueue();
for(int i=0; i<candidates; i++) {
pre.offer(costs[i]);
aft.offer(costs[n-1-i]);
}
int p1 = candidates;
int p2 = n-1-candidates;
// 模拟k次
while(k>0) {
k --;
if( pre.isEmpty() || aft.isEmpty() ) return ans;
if( pre.peek() <= aft.peek() ) {
ans += pre.poll();
if(p1<=p2) {
pre.offer(costs[p1]);
p1 ++;
}
} else {
ans += aft.poll();
if(p2>=p1) {
aft.offer(costs[p2]);
p2 --;
}
}
}
return ans;
}
}
857. 雇佣 K 名工人的最低成本
有 n 名工人。 给定两个数组 quality 和 wage ,其中,quality[i] 表示第 i 名工人的工作质量,其最低期望工资为 wage[i] 。
现在我们想雇佣 k 名工人组成一个工资组。在雇佣 一组 k 名工人时,我们必须按照下述规则向他们支付工资:
对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
工资组中的每名工人至少应当得到他们的最低期望工资。
给定整数 k ,返回 组成满足上述条件的付费群体所需的最小金额 。在实际答案的 10-5 以内的答案将被接受。
思路:计算质量的平均工资r=wage/quality,按照r对id进行排序。那么代价cost=sumQuality*r。显然r会越来越大,那么只能让sumQuality变小,采用最大堆实现该过程。
class Solution {
public double mincostToHireWorkers(int[] quality, int[] wage, int k) {
int n = wage.length;
// 按照r值对,数组ids进行排序
Integer[] ids = new Integer[n];
for(int i=0; i<n; i++) {
ids[i] = i;
}
// 数组存的是对象
Arrays.sort(ids, (i, j)-> wage[i]*quality[j]-wage[j]*quality[i] );
// 先选前k个工人
int sum = 0;
Queue<Integer> q = new PriorityQueue<Integer>((a,b)->b-a);
for(int i=0; i<k; i++) {
int id = ids[i];
sum += quality[id];
q.offer(quality[id]);
}
double ans = ((double)wage[ids[k-1]]/quality[ids[k-1]])*sum;
// 要使ans更小,那么sum必须减小,也就是大顶堆要更新
for(int i=k; i<n; i++) {
int id = ids[i];
if(quality[id]<q.peek()) {
sum = sum - q.poll();
q.offer(quality[id]);
sum = sum + quality[id];
ans = Math.min(ans, ((double)wage[id]/quality[id])*sum);
}
}
return ans;
}
}