857. 雇佣 K 名工人的最低成本
有 n 名工人。 给定两个数组 quality 和 wage ,其中,quality[i] 表示第 i 名工人的工作质量,其最低期望工资为 wage[i] 。
现在我们想雇佣 k 名工人组成一个工资组。在雇佣 一组 k 名工人时,我们必须按照下述规则向他们支付工资:
- 对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
- 工资组中的每名工人至少应当得到他们的最低期望工资。
给定整数 k ,返回 组成满足上述条件的付费群体所需的最小金额 。在实际答案的 10-5 以内的答案将被接受。。
示例 1:
输入: quality = [10,20,5], wage = [70,50,30], k = 2
输出: 105.00000
解释:我们向 0 号工人支付 70,向 2 号工人支付 35。
示例 2:
输入: quality = [3,1,10,10,1], wage = [4,8,2,2,7], k = 3
输出: 30.66667
解释: 我们向 0 号工人支付 4,向 2 号和 3 号分别支付 13.33333。
提示:
n == quality.length == wage.length
1 <= k <= n <= 1e4
1 <= quality[i],wage[i] <= 1e4
解析:
- 可以证明花费资金最少时一定有一名工人拿到最低工资
- 反证:如果没有人拿到最低工资,那么将那个人工资降到最低工资,花费资金更少。
- 选取的k个人中,如何计算工资呢?
- 记 r=w/q , sum_q=q+q1+…+qk-1工资应该为cost = sum_q*r. 其中r,应该选择这k个人中最大的,只有这样才满足第一条要求。
- 我们可以按照r=w/q从小到大排序, 接着我们可以枚举选出这个人;前k-1个人一定不会拿到最低工资,也就是说r一定不可以从前k-1个人中选,因为第k个人的 r 会更大。因此,我们只需要枚举后k-1之后的人拿最低工资即可。
- 枚举后k-1之后的人,用大根堆记录前k-1个最小的q,不断更新q,计算cost。
代码:
class Solution {
public:
// 使用prority_queue<>宣传第k性价比,然后按照此性价比找k-1个元素,性价比更大的按照从小到大选取最合适的钱数
double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k) {
int n=quality.size();
vector<pair<double,double>> vp;
for(int i=0;i<n;i++){
vp.push_back({quality[i],wage[i]});
}
// 按照r = w/q从小到大排序
sort(vp.begin(),vp.end(),[&](pair<double,double>& l,pair<double,double>& r){
return l.second*r.first<r.second*l.first;
});
double sum=0;
priority_queue<double> pq;
// 将前k-1个人的q放入堆中,计算sum_q
// 这前k-1个人肯定不会发最低工资,第k个人会更大
for(int i=0;i<k;i++){
pq.push(vp[i].first);
sum+=vp[i].first;
}
double res=sum*vp[k-1].second/vp[k-1].first;
// 不断枚举r大的人,能保证前边人的r小于当前枚举的人
// 当前人发最低工资的花费为:sum*(vp[i].second*1.0/q);
for(int i=k;i<n;i++){
// r变大,如果q不变小,结果肯定不会小的
if(int q = vp[i].first;q<pq.top()){
sum-=pq.top()-q;
pq.pop();
pq.push(q);
res=min(res,sum*(vp[i].second*1.0/q));
}
}
return res;
}
};