LeetCode2462题(个人解法以及官方解法详解)

文章讨论了在给定成本数组和雇佣规则下,如何使用小根堆数据结构高效地找出每轮雇佣中代价最小的工人,最终计算出雇佣k位工人的总代价。算法涉及动态维护前后candidates数量范围内的最小代价元素。
摘要由CSDN通过智能技术生成

给你一个下标从 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 位工人的总代价。

输入:costs = [17,12,10,2,7,2,11,20,8], k = 3, candidates = 4
输出:11
解释:我们总共雇佣 3 位工人。总代价一开始为 0 。
- 第一轮雇佣,我们从 [17,12,10,2,7,2,11,20,8] 中选择。最小代价是 2 ,有两位工人,我们选择下标更小的一位工人,即第 3 位工人。总代价是 0 + 2 = 2 。
- 第二轮雇佣,我们从 [17,12,10,7,2,11,20,8] 中选择。最小代价是 2 ,下标为 4 ,总代价是 2 + 2 = 4 。
- 第三轮雇佣,我们从 [17,12,10,7,11,20,8] 中选择,最小代价是 7 ,下标为 3 ,总代价是 4 + 7 = 11 。注意下标为 3 的工人同时在最前面和最后面 4 位工人中。
总雇佣代价是 11 。
输入:costs = [1,2,4,1], k = 3, candidates = 3
输出:4
解释:我们总共雇佣 3 位工人。总代价一开始为 0 。
- 第一轮雇佣,我们从 [1,2,4,1] 中选择。最小代价为 1 ,有两位工人,我们选择下标更小的一位工人,即第 0 位工人,总代价是 0 + 1 = 1 。注意,下标为 1 和 2 的工人同时在最前面和最后面 3 位工人中。
- 第二轮雇佣,我们从 [2,4,1] 中选择。最小代价为 1 ,下标为 2 ,总代价是 1 + 1 = 2 。
- 第三轮雇佣,少于 3 位工人,我们从剩余工人 [2,4] 中选择。最小代价是 2 ,下标为 0 。总代价为 2 + 2 = 4 。
总雇佣代价是 4 。

本人题解是这个,在最后132个用例后出现超时。

class Solution {
    public long totalCost(int[] costs, int k, int candidates) {
        int min_index=0,min_index_pre=0,min_index_last=costs.length;
        long sum=0;
        int len=costs.length;
        while(k>0){
            //如果candidates的数量小于costs数组
            if(len>=candidates){
                //前candidates的数量
                min_index_pre=minCost(costs,candidates,len,false);
                //后candidates的数量
                min_index_last=minCost(costs,candidates,len,true);
                if(costs[min_index_pre]<=costs[min_index_last]){
                    sum+=costs[min_index_pre];
                    costs=updateArray(costs,min_index_pre);
                    len--;
                    k--;
                }
                else{
                    sum+=costs[min_index_last];
                    costs=updateArray(costs,min_index_last);
                    len--;
                    k--;
                }
            }
            //若costs数组人数少于candidates,选择最少的
            else{
                min_index=minCost(costs,len,len,false);
                sum+=costs[min_index];
                costs=updateArray(costs,min_index);
                len--;
                k--;
            }
    }
        return sum;
    }
    //调整数组下标
    public int[] updateArray(int[] array,int index){
        int temp=array[index];
        for(int i=index;i<array.length-1;i++){
            array[i]=array[i+1];
        }
        array[array.length-1]=temp;
        return array;
    }
    /**根据传入数组的长度,寻找数组最小的下标
     */
    public int minCost(int[] array,int num,int length,boolean flag){
        int min,index,array_num;
        if(flag==false){
            min=array[0];
            index=0;
            for(int i=1;i<num;i++){
            if(min>array[i]){
                min=array[i];
                index=i;
                }
            }
        }
        else{
            min=array[length-1];
            index=length-1;
            array_num=length-1-num;
            for(int i=length-1;i>array_num;i--){
                if(min>array[i]){
                    min=array[i];
                    index=i;
                }
            }
        }
        return index; 
}
}

官方解法 :通过构造小根堆,通过把前candidates个元素和后candidates个元素加入构造小根堆,不断通过获取堆的根顶元素来获取最小代价。

小根堆:即先把元素插入时,按照完全二叉树来插入。每插入一个元素,则通过比对父节点和子节点的代价和序号来进行排列,代价和序号较小的则为父节点,并通过交换位置不断构造。

以costs =[17,12,10,2,7,2,11,20,8],k=3,candidates=4为例子:刚开始构造的最小堆为下:

 public static long totalCost(int[] costs, int k, int candidates) {
        int n = costs.length;
        //建立小根堆,堆里每一个元素是数组,数组第一个是代价,第二个是costs的序号
        //加入元素时,会按照小根堆的方式构造小根堆:先比较代价,后比较序号
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
        int left = candidates - 1, right = n - candidates;
        //前后candidates的元素个数小于总元素个数
        if (left + 1 < right) {
            //取前candidates元素加入堆
            for (int i = 0; i <= left; ++i) {
                pq.offer(new int[]{costs[i], i});
            }
            //取后candidates元素加入堆
            for (int i = right; i < n; ++i) {
                pq.offer(new int[]{costs[i], i});
            }
        }
        //前后candidates的元素个数大于总元素个数,相当于把全部元素加入小根堆
         else {
            for (int i = 0; i < n; ++i) {
                pq.offer(new int[]{costs[i], i});
            }
        }
        //总代价
        long ans = 0;
        //通过循环得到每次的最小代价
        for (int i = 0; i < k; ++i) {
            //取出小根堆堆顶元素,此时就为前后candidates中代价元素最小的
            int[] arr = pq.poll();
            int cost = arr[0], id = arr[1];
            ans += cost;
            //前candidates和后candidates中间还有元素的话
            if (left + 1 < right) {
                //取到的最小代价是左边的元素,就left往后的一个元素加入构造小根堆
                if (id <= left) {
                    ++left;
                    pq.offer(new int[]{costs[left], left});
                } else {
                    //取到的最小代价是右边的元素,就right往前一个元素加入构造小根堆
                    --right;
                    pq.offer(new int[]{costs[right], right});
                }
            }
        }
        return ans;
    }

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程的小李

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

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

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

打赏作者

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

抵扣说明:

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

余额充值