Leetcode 每日一题:Minimum Cost to Hire K Workers

写在前面:

今天来看一道比较困难的题目, Minimum Cost to Hire K Workers。这道较新的 Leetcode Hard 的题目是我迄今为止分享过的最难的一道题,对题目的理解,关系的归纳,以及对数据结构的运用都有很高的要求。对于答题者的数学思维也有不低的要求和考量,如果不能正确运用数学推演简化题目其中的关系的话,解开这道题(或者做出 AC 的答案) 不太容易

同时,这道题目的背景和当下打工人的困境一拍即合。这道题目的本质就是用最少的钱招足够的工产生最多的价值 -- 妥妥就是一个降本增效的绩效考核算法呀这不是~~ 作为被优化最频繁的岗位,咱程序员码农可真是做到自己的人生题了哈哈哈~~

不仅仅是这道题目有趣的背景,他的价值同样不菲,在最近三个月内连续出现在 Google 的面试真题当中,近半年内也在 Amazon, Meta 等多个大厂的面试题目中出现。可以看出这道题目在考察学生综合能力的层面非常全面。那让我们也跟随这篇文章一起来探索一下他的解法吧!

题目介绍:

题目信息:

题目问题:

  • 给定两个长度相等的数组, 每个位置代表同一个 worker 的 quality 和 wage,一一对应:
    • ​​​​​​​Quality: [10, 20, 5]
    • Wage: [70, 50, 30]
  • 给定一个整数 K,代表我们需要雇佣的 worker 个数,满足条件的雇佣组被称为 paid group
  • 同一个 paid group 的限定条件
    • 在同一个雇佣组中,每一个工人都必须至少获得他们所期望的最小工资(也就是 wage 数组中对应每一个 worker 位置的工资)
    • 在同一个雇佣组中,每一个工人的工资必须和他们的劳动 quality 成正比,比如,如果雇员 A 的 Quality 是 雇员 B 的两倍,则雇员 A 的工资,在同时满足条件1的情况下,必须要是雇员 B 的两倍
  • 求出在所有满足条件的 paid group 中,花销最少的那个组,返回最小需要的工资

题目想法:

这道题乍一看非常难,因为有两个限定条件,就涉及不仅要选工资最便宜的人进行工作,同时还得考虑可能工资低的人可能 Quality 会更高,这样导致低期望工资的选择不一定带来低的成本

Wage 与 Quality 比率:

既然总的雇佣开销同时受 Quality 和 Wage 的影响,我们何不计算出每一位工人的单位 Quality 所需工资呢,这样的话,我们只要 minimize 这个比率,就可以同时做到考虑低工资的同时考虑低quality。更小的单位 Quality 工资 意味着更少的 综合成本(怎么听着那么耳熟,这不就是降本增效吗????)

总 Quality:

我们不仅要从 “降本增效” 的角度请单价最便宜的工人,同时,因为我们整组的工资的最小值一定是工资最高 (也就是 Wage Quality 比率最高)的那一位工人的(满足条件1),所以更小的 quality 也是我们降低总成本的一个考量维度。

结合在一起:

将两个考量指标结合在一起以后,我们就可以得出,我们需要同时在 wage Quality 和 总 Quality 纬度上取最小即可:

  • wage Quality Ratio 取最小,可以先遍历数组进行 ratio 的计算,然后将所有的 ratio 进行排序
  • 在选择最小的 ratios 的时候,同时将选择的 quality 进行排序,当我们有超过 k 人的时候,总是删除 quality 最多的那个人,并计算判断是否获得了最小的情况
  • 我们可以使用 Max heap 对 quality 进行轻松的排序

Tips:

如果觉得我讲的不够清楚的话,也可以上 Leetcode 官方解析查看(但好像要会员),地址在:

https://leetcode.com/problems/minimum-cost-to-hire-k-workers/editorial/

题目解法:

  • 定义 Maxheap,WageToQuality Ratio 向量
  • 遍历所有 wage 和 quality 内元素:
    • wageToQuality[i] = pair{double(wage[i]) / quality[i], quality[i]}
  • sort WageToQualityRatio vector
  • 遍历 WageToQuality vector:
    • Maxheap.push(WageToQuality[i].second)
    • totalQuality += WageToQuality[i].second
    • 如果 Maxheap 长度超过 k:
      • totalQuality 减去 Maxheap 最上面数
      • Maxheap.pop()
    • 如果 Maxheap 长度 = k:
      • res = min(res, totalQuality * WageToQuality[i][0])
  • 返回 res 结果

题目代码:

class Solution {
public:
    double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k) {
        vector<pair<double, int>> wageToQualityRatio;
        int workerNum = quality.size();
        double minRes = numeric_limits<double>::max();
        
        // retrieve the work wage ratio for each worker
        for(int i = 0; i < workerNum; i++){
            wageToQualityRatio.push_back({static_cast<double>(wage[i])/quality[i], quality[i]});
        }
        // this ensures that when we iterate from left to right, the current index's wage quality ratio will satisfy every worker's wages expectation on the left
        sort(wageToQualityRatio.begin(), wageToQualityRatio.end());
        
        //define a maxheap for quality comparison
        priority_queue<int> workerQuality;
        int totalQuality = 0;
        for(int i = 0; i < wageToQualityRatio.size(); i++){
            workerQuality.push(wageToQualityRatio[i].second);
            totalQuality += wageToQualityRatio[i].second;
            
            // too many workers, pop the worker with the most quality since the wage quality ratio is fixed at each step
            if(workerQuality.size() > k){
                totalQuality -= workerQuality.top();
                workerQuality.pop();
            }
            
            // whenever we have enough worker, calculate and see if it is minimum
            if(workerQuality.size() == k){
                minRes = min(minRes, wageToQualityRatio[i].first * totalQuality);
            }
        }
        return minRes;
    }
};
  • Runtime: O(nlogn) + O(nlogk)
    • 排序 wageQualityRatio:O(nlogn)
    • 遍历,并在每一个位置添加删改 Maxheap:O(nlogk)
  • Space: O(n) + O(k)
  • Tips:对于最大的 double:没有 DOUBLE_MAX, 可以使用:numeric_limits<double>::max()
  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值