leetcode
leetcode 2151 ~ 2200
雇佣 K 位工人的总代价

雇佣 K 位工人的总代价

难度:

标签:

题目描述

代码结果

运行时间: 107 ms, 内存: 24.9 MB


/*
 * Problem: Given an array `costs`, hire `k` workers based on the following rules:
 * 1. In each round, hire one worker from the first `candidates` and the last `candidates` workers.
 * 2. Choose the worker with the minimum cost. If there's a tie, choose the worker with the smaller index.
 * 3. Return the total cost of hiring `k` workers.
 */
import java.util.*;
import java.util.stream.IntStream;

public class SolutionStream {
    public int totalCost(int[] costs, int k, int candidates) {
        int totalCost = 0;
        PriorityQueue<Integer> minHeap = new PriorityQueue<>((a, b) -> costs[a] == costs[b] ? a - b : costs[a] - costs[b]);
        for (int i = 0; i < candidates; i++) minHeap.offer(i);
        for (int i = costs.length - 1; i >= costs.length - candidates && i >= candidates; i--) minHeap.offer(i);
        Set<Integer> used = new HashSet<>();
        while (k > 0 && !minHeap.isEmpty()) {
            int idx = minHeap.poll();
            if (used.add(idx)) {
                totalCost += costs[idx];
                k--;
                if (idx >= candidates && idx < costs.length - candidates) {
                    if (idx - candidates >= 0) minHeap.offer(idx - candidates);
                    if (idx + candidates < costs.length) minHeap.offer(idx + candidates);
                }
            }
        }
        return totalCost;
    }
}

解释

方法:

该题解的思路是使用两个最小堆(left_heap和right_heap)来分别管理数组costs中的前candidates个元素和后candidates个元素。每次迭代中,从这两个堆中找出最小的元素,然后将其从堆中移除,并将其加入到总的代价中。同时,堆从costs中继续添加新的元素以保持堆的大小为candidates,直到完成k次选择。通过这种方式,可以有效地在每一轮中找到最小代价的工人并加以雇佣。

时间复杂度:

O(k log(candidates))

空间复杂度:

O(candidates)

代码细节讲解

🦆
在`totalCost`函数中,为什么选择使用两个最小堆来管理`costs`数组的前后`candidates`个元素?是否有其他数据结构可行?
使用两个最小堆是为了能够高效地从`candidates`个候选者中选出最小的元素。最小堆可以在常数时间O(1)内得到最小元素,并且在对数时间O(log n)内进行插入和删除操作。这使得每次选择操作都能快速进行。虽然也可以考虑使用排序数组或平衡二叉搜索树,但在动态更新(即添加或删除元素)时,最小堆提供了更优的性能。排序数组虽然可以快速访问最小元素,但插入和删除操作平均需要O(n)时间。平衡二叉搜索树虽然提供了O(log n)的插入、删除和查找时间,但实现复杂度高于最小堆。因此,最小堆在本问题中是最合适的选择。
🦆
堆的初始化和更新是如何确保每一轮都能从最前面`candidates`和最后面`candidates`人中选出代价最小的工人?
堆的初始化首先将`costs`数组中前`candidates`个元素和后`candidates`个元素分别放入两个最小堆中,并进行堆化处理。这样,两个堆的根节点即是各自堆中的最小值。在每次选择工人时,比较两个堆的根节点,选择其中较小的一个,然后将其从堆中移除。移除操作后,为保持堆的大小为`candidates`,会从`costs`数组中相应地向堆中添加新的元素。这种方式确保了每轮都可以从当前候选的前`candidates`和后`candidates`中选出最小代价的工人。
🦆
如何处理`costs`数组长度小于两倍`candidates`的情况,即当前后`candidates`有重叠时的特殊处理?
当`costs`数组长度小于两倍`candidates`时,前`candidates`个元素和后`candidates`个元素会有重叠。在这种情况下,可以选择将整个`costs`数组放入一个最小堆中。由于数组长度小于两倍`candidates`,这意味着所有元素都是候选元素。因此,每次从堆中取出最小元素即可,直到完成k次选择。这种方法简化了处理过程,同时依旧保证了每次都能选出当前代价最小的工人。

相关问题