n
名工人。给定两个数组 quality
和 wage
,其中,quality[i]
表示第 i
名工人的工作质量,其最低期望工资为 wage[i]
。k
名工人组成一个工资组。在雇佣一组 k
名工人时,我们必须按照下述规则向他们支付工资:
k
,返回组成满足上述条件的付费群体所需的最小金额。
10^{-5}
以内的答案将被接受。k
个人,如何使得所有人工资都满足期望值,同时工资总值最低?答案是按照员工工作的性价比,第 i
个人的性价比可以由 ratio[i] = wage[i]/quality[i]
得出,即每单位工作质量的工资。【注意 ratio[i]
越低,性价比越高】k
个人一组,只需要以 ratio[i]
最高的工人作为基准即可满足条件;而假设这 k
名工人工资总和为 sum_q
,则发的工资总额为 sum_q * ratio[i]
。【即以最高标准给所有工人发工资,标准本身就是最低期望值】ratio
排序(从小到大排序),接着再维护一个大小为 k
的最大堆即可确认选择的 k
个人选(堆中存放工人的工作质量)。
ratio
从小到大的方式顺序遍历工人,当堆中已经有 k
个元素,而当前质量 quality[i]
低于堆顶质量,此时可以弹出堆顶,将 quality[i]
入堆。【因为此时访问的工人 ratio[i]
更高,此时只有更低的质量才有可能挤掉栈顶的工人】class Solution
{
public:
double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k)
{
int m = quality.size();
vector<int> id(m);
iota(id.begin(), id.end(), 0); // 将数组 id 用 [0, m-1] 填充
sort(id.begin(), id.end(), [&](const int& i, const int& j)
{
return wage[i] * quality[j] < wage[j] * quality[i]; // 判断 w[i]/q[i] < w[j]/q[j]
});
priority_queue<int> pq; // 优先队列默认为大根堆
int sum_q = 0; // k 个工人的总质量
for(int i = 0; i < k; ++i) pq.push(quality[id[i]]), sum_q += quality[id[i]];
double ans = sum_q * ((double) wage[id[k-1]] / quality[id[k-1]]); // 选择 ratio 最小的 k 个工人一组
for(int i = k; i < m; ++i) // 剩余工人
{
int q = quality[id[i]];
if(q < pq.top()) // 此时 ratio 比前面的更大,因此总质量取更小值,才有可能有更优值
{
sum_q -= pq.top(); sum_q += q; // 当前质量存入,弹出优先队列的最大质量
pq.pop(); pq.push(q);
ans = min(ans, sum_q * ((double) wage[id[i]] / q));
}
}
return ans;
}
};