857. 雇佣 K 名工人的最低成本--(每日一难phase2--day12)

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;
    }
};





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值