LeetCode 857. Minimum Cost to Hire K Workers C++实现

题目描述

现在有n个工人的工作效率和最低薪资的信息。你需要从中招k个工人,你给这k个工人的薪资需要满足以下两点:

  1. 必须高于工人的最低薪资
  2. 工人之间的薪资比必须和他们的工作效率比一致

试求你招k个工人需要支付的最少薪资总数是多少?

示例

Input: quality = [10,20,5], wage = [70,50,30], k = 2
Output: 105.00000
Explanation: We pay 70 to 0th worker and 35 to 2nd worker.

题目分析

每个工人薪资的计算

首先假设我们已经选出来k个工人是谁,如何计算要给每个工人多少钱?

其实就是优先满足“性价比”最低的人,再根据“性价比”最低的人按比给其他工人定价。

拿示例举例,假设我们选定了第0个工人和第2个工人,我们可以算出两个工人的“性价比”(quality / wage)分别为:1/7和1/6,因此第0个工人的性价比最低,以他为基准给出价格。所以得到两个工人薪资分别为70和35。

所以对于这些工人,我们通过他们的“性价比”由低到高的顺序进行排序。按照这个顺序来选择工人的话总是可以先选到“性价比”最低的工人,也就是说我们可以先定好给价的基准。

怎样才能给工人的钱尽量少?

假设通过上述步骤我们已经确定了“性价比”最低的工人,那么剩下的工人应该怎么选择?其实就是从那些“性价比”更高的工人中选择工作效率低的工人,越低付的钱越少。注意是从“性价比”更高的工人中选择,因为一旦选了“性价比”更低的工人,我们的假设(确定了“性价比”最低的工人)就被破坏了。

思路总结

所以我们如果想找到最小花销,可以先按照工人的性价比由高到低排序,然后按照这个顺序选择工人。这样有一个好处:当前步骤加入选择名单的一定是性价比最低的工人。如果当前已经选了k个工人但是还需要将新的工人加入到选择名单中,那么就从已选的k个里面剔除工作效率最高的工人,然后把新的人选加入到选择名单中。注意每次选择了k个工人的时候都计算一下总价,记录最少的总价即为题目的解。

代码实现

class Solution {
public:
    static bool cmp(pair<double, int>& a, pair<double, int>& b){
        return a.first > b.first;
    }
    
    
    struct cmp_quality{
      bool operator()(int& a, int& b){
          return a < b;
      }  
    };
    
    double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k) {
        // 性价比序列,first是性价比数值,second是在原数组中的索引值
        vector<pair<double, int>> q_w;
        for(int i = 0; i < quality.size(); i++){
            // 计算性价比
            double cost_performance = double(quality[i]) / double(wage[i]);
            pair<double, int> cur_performance(cost_performance, i);
            q_w.push_back(cur_performance);
        }
        // 对性价比序列进行排序
        sort(q_w.begin(), q_w.end(), cmp);
        
        // 按照性价比顺序选择工人
        // 建立一个最大堆,只记录quality,因为wage可以根据quality和性价比算出来
        priority_queue<int, vector<int>, cmp_quality> cur_select;
        // 记录当前的性价比
        double cur_cp = 0;
        // 记录当前选择的工人的quality总和
        int cur_quality_sum = 0;
        // 最小花销
        double mincost = DBL_MAX;
        for(int i = 0; i < q_w.size(); i++){
            // 插入最新值
            pair<double, int> cur_worker = q_w[i];
            int index = cur_worker.second;
            cur_select.push(quality[index]);
            // quality总和更新
            cur_quality_sum += quality[index];
            // 性价比更新
            cur_cp = cur_worker.first;
            // 如果选了k个工人,计算总价更新然后剔除quality最高的
            if(cur_select.size() == k){
                // 如果总价比记录的小,就更新
                if(mincost > double(cur_quality_sum) / cur_cp)
                    mincost = double(cur_quality_sum) / cur_cp;
                // 删除quality最高的
                cur_quality_sum -= cur_select.top();
                cur_select.pop();
            }
        }
        
        return mincost;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值