【LeetCode】最小堆、最大堆相关题目

2462. 雇佣 K 位工人的总代价

给你一个下标从 0 开始的整数数组 costs ,其中 costs[i] 是雇佣第 i 位工人的代价。

同时给你两个整数 k 和 candidates 。我们想根据以下规则恰好雇佣 k 位工人:

总共进行 k 轮雇佣,且每一轮恰好雇佣一位工人。
在每一轮雇佣中,从最前面 candidates 和最后面 candidates 人中选出代价最小的一位工人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
比方说,costs = [3,2,7,7,1,2] 且 candidates = 2 ,第一轮雇佣中,我们选择第 4 位工人,因为他的代价最小 [3,2,7,7,1,2] 。
第二轮雇佣,我们选择第 1 位工人,因为他们的代价与第 4 位工人一样都是最小代价,而且下标更小,[3,2,7,7,2] 。注意每一轮雇佣后,剩余工人的下标可能会发生变化。
如果剩余员工数目不足 candidates 人,那么下一轮雇佣他们中代价最小的一人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
一位工人只能被选择一次。
返回雇佣恰好 k 位工人的总代价。


采用最小堆

class Solution {
    public long totalCost(int[] costs, int k, int candidates) {
        long ans = 0;
        int n = costs.length;
        if(2*candidates+k>n) {
            Arrays.sort(costs);
            for(int i=0; i<k; i++) ans += costs[i];
            return ans;
        }
        Queue<Integer> pre = new PriorityQueue();
        Queue<Integer> aft = new PriorityQueue();
        for(int i=0; i<candidates; i++) {
            pre.offer(costs[i]);
            aft.offer(costs[n-1-i]);
        }
        int p1 = candidates;
        int p2 = n-1-candidates;
        // 模拟k次
        while(k>0) {
            k --;
            if( pre.isEmpty() || aft.isEmpty() ) return ans;
            if( pre.peek() <= aft.peek() ) {
                ans += pre.poll();
                if(p1<=p2) {
                    pre.offer(costs[p1]);
                    p1 ++;
                }
            } else {
                ans += aft.poll();
                if(p2>=p1) {
                    aft.offer(costs[p2]);
                    p2 --;
                }
            }
        }

        return ans;
    }
}

857. 雇佣 K 名工人的最低成本

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

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

对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
工资组中的每名工人至少应当得到他们的最低期望工资。
给定整数 k ,返回 组成满足上述条件的付费群体所需的最小金额 。在实际答案的 10-5 以内的答案将被接受。


思路:计算质量的平均工资r=wage/quality,按照r对id进行排序。那么代价cost=sumQuality*r。显然r会越来越大,那么只能让sumQuality变小,采用最大堆实现该过程。

class Solution {
    public double mincostToHireWorkers(int[] quality, int[] wage, int k) {
        int n = wage.length;

        // 按照r值对,数组ids进行排序
        Integer[] ids = new Integer[n];
        for(int i=0; i<n; i++) {
            ids[i] = i;
        }

        // 数组存的是对象
        Arrays.sort(ids, (i, j)-> wage[i]*quality[j]-wage[j]*quality[i] );

        // 先选前k个工人
        int sum = 0;
        Queue<Integer> q = new PriorityQueue<Integer>((a,b)->b-a);
        for(int i=0; i<k; i++) {
            int id = ids[i];
            sum += quality[id];
            q.offer(quality[id]);
        }        


        double ans = ((double)wage[ids[k-1]]/quality[ids[k-1]])*sum;

        // 要使ans更小,那么sum必须减小,也就是大顶堆要更新
        for(int i=k; i<n; i++) {
            int id = ids[i];
            if(quality[id]<q.peek()) {
                sum = sum - q.poll();
                q.offer(quality[id]);
                sum = sum + quality[id];
                ans = Math.min(ans, ((double)wage[id]/quality[id])*sum);
            }
        }


        return ans;


    }
}
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

P.H. Infinity

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值