LeetCode习题2462详细注释解析

151 阅读4分钟

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

解决方法一:暴力破解

首先想到的是采用暴力破解的方法,在数组长度大于candidates时,遍历前candidates和后candidates个元素,之后手动维护数组的元素长度。当长度小于candidates时,直接遍历找到最小值即可,最终测试用例超时

import java.util.Arrays;

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public long totalCost(int[] costs, int k, int candidates) {
        long count = 0; //计算总花费
        /*
        因为这里我们一直开销一定是大于0的,因此当我们取出来元素后
         */
        int number = 0;  //雇佣工人的数量
        int length = costs.length; //记录数组的实际长度
        while (number != k) {
            if (length > candidates) {
                int min = costs[0]; //存储最小代价
                int location = 0; //存储下标
                for (int i = 0; i < candidates; i++) { //遍历找到最小值
                    if (costs[i] < min) {
                        min = costs[i];
                        location = i;
                    }
                }
                for (int i = length - candidates; i < length; i++) {
                    if (costs[i] < min) {
                        min = costs[i];
                        location = i;
                    }
                }
                count = count + min; //计算当前的花费
                for (int i = location; i < length - 1; i++) { //修改数组的长度
                    costs[i] = costs[i + 1];
                }
                length--;
            } else {
                int min = 100001; //因为花费最大的是10000;这样设置设为了避免cost[0] = 0
                int location = 0;
                for (int i = 0; i < length; i++) {
                    if (costs[i] != 0 && costs[i] < min) {
                        min = costs[i];
                        location = i;
                    }
                }
                count = count + min;
                costs[location] = 0;
            }
            number++;

        }
        return count;
    }
}

方法二:优先队列

这种方法根据Java的优先队列来实现,优先级队列的头部是基于自然排序或基于比较器的排序的最小元素。当我们轮询队列时,它从队列中返回头对象。

import java.util.Arrays;
import java.util.PriorityQueue;

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public long totalCost(int[] costs, int k, int candidates) {
        //创建优先队列1,主要存储0~candidates-1的元素
        PriorityQueue<Integer>priorityQueue1 = new PriorityQueue<>();
        //创建优先队列2,主要存储length-candidates的元素
        PriorityQueue<Integer>priorityQueue2 = new PriorityQueue<>();
        //这两个参数的主要作用是确保两个优先队列的元素不重复
        int l = candidates-1;  //指示优先队列1的尾部
        int r = costs.length - candidates;//指示优先队列2的头部
        long count = 0l; //计算总开销
        //向优先队列1中存放元素
        for(int i = 0;i < candidates; i++){
            priorityQueue1.offer(costs[i]);
        }
        if(l>=r){ //这种情况下表示左candidates和右candidates部分有交集,要把交集去掉
            for(int i = l+1; i<costs.length; i++){
                priorityQueue2.offer(costs[i]);
            }
        }else{//此时无交集,直接从r插入
            for(int i = r; i<costs.length; i++){
                priorityQueue2.offer(costs[i]);
            }
        }
        //现在优先队列创建完成了,我们只需要比较队头元素即可,因为优先队列从低到高有序
        //当两个队列均不为空的时候执行该循环
        while(k>0&& !priorityQueue1.isEmpty() && !priorityQueue2.isEmpty()){
            if(priorityQueue1.peek()<=priorityQueue2.peek()){
                count += priorityQueue1.poll();
                //++l,是判断l的下一个元素在不在r里边
                if(++l<r){//说明没有交集,删除后需要补偿至candidates大小
                    priorityQueue1.offer(costs[l]);
                }
            }else{
                count += priorityQueue2.poll();
                //--r,是判断r的前一个元素在不在l里边
                if(--r>l){
                    priorityQueue2.offer(costs[r]);
                }
            }
            k--; //此时已经选出来一个工人了
        }
        //此时表明人还没有选完
        while(k>0){
            if(!priorityQueue1.isEmpty()){
                count += priorityQueue1.poll();
            }
            if(!priorityQueue2.isEmpty()){
                count += priorityQueue2.poll();
            }
            k--;
        }
        return count;
    }
}