初学python记录:力扣857. 雇佣 K 名工人的最低成本

题目:

有 n 名工人。 给定两个数组 quality 和 wage ,其中,quality[i] 表示第 i 名工人的工作质量,其最低期望工资为 wage[i] 。

现在我们想雇佣 k 名工人组成一个工资组。在雇佣 一组 k 名工人时,我们必须按照下述规则向他们支付工资:

  1. 对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
  2. 工资组中的每名工人至少应当得到他们的最低期望工资。

给定整数 k ,返回 组成满足上述条件的付费群体所需的最小金额 。在实际答案的 10^-5 以内的答案将被接受。

提示:

  • n == quality.length == wage.length
  • 1 <= k <= n <= 104
  • 1 <= quality[i], wage[i] <= 104

思考:

暴力解法

由题意可知,一定至少有一个人(i)发的工资正好等于期待的工资wage[i],那么其他人(x)发的工资按比例即为:\frac{wage[i]}{quality[i]}*quality[x],同时发的工资要满足大于等于wage[x]的条件。将满足条件的实发工资从小到大排序,前k个之和即为选的k个人的工资和sum,遍历i得到的sum的最小值即为答案。代码如下:

class Solution:
    def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float:
        n = len(quality)
        sum_dict = {}
        for i in range(n):
            pay = []
            for x in range(n):
                pay_x = wage[i] / quality[i] * quality[x]    # 按比例实发工资
                if pay_x >= wage[x]:    # 实发工资大于等于期望工资则加入候选
                    pay.append(pay_x)
            if len(pay) >= k:   
                # 若满足条件不足k人,则跳到下一人;否则排序,取前k个工资之和
                pay.sort()
                sum_dict[i] = sum(pay[:k])
        return min(sum_dict.values())

超时,卡在第 41 / 46 个例子:

优化 ——最小堆

代码如下,思路在注释里:

class Solution:
    def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float:
        n = len(wage)
        candidates = sorted(zip(wage, quality), key = lambda candidate:candidate[0]/candidate[1])     # 按r=wage/quality从小到大排序
        ans = float('inf')
        for i in range(k-1, n):
            r = candidates[i][0]/candidates[i][1]   # 基准r
            Q = candidates[i][1]    # quality之和,初始化为i的quality
            # 当前考虑的 candidate 列表的前 i 个元素
            current_candidates = candidates[:i]
            # 获取这些元素的 quality 并创建一个最小堆
            qualities = [qual for _, qual in current_candidates]
            heapq.heapify(qualities)
            # 取最小的 k-1 个 quality 的和,加给Q
            Q += sum(heapq.nsmallest(k-1, qualities))
            res = Q * r
            if res < ans:
                ans = res

        return ans

但是还是不够快,这里参考灵神的写法:

class Solution:
    def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float:
        pairs = sorted(zip(quality, wage), key=lambda p: p[1] / p[0])  # 按照 r 值排序
        h = [-q for q, _ in pairs[:k]]  # 加负号变成最大堆
        heapify(h)
        sum_q = -sum(h)
        ans = sum_q * pairs[k - 1][1] / pairs[k - 1][0]  # 选 r 值最小的 k 名工人
        for q, w in pairs[k:]:  # 后面的工人 r 值更大
            if q < -h[0]:  # 但是 sum_q 可以变小,从而可能得到更优的答案
                sum_q += heapreplace(h, -q) + q  # 更新堆顶和 sum_q
                ans = min(ans, sum_q * w / q)
        return ans

作者:灵茶山艾府
链接:https://leetcode.cn/problems/minimum-cost-to-hire-k-workers/solutions/1815856/yi-bu-bu-ti-shi-ru-he-si-kao-ci-ti-by-en-1p00/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

T T算法之路漫漫啊,提交通过:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值