Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,70 +1,37 @@
package g2401_2500.s2462_total_cost_to_hire_k_workers;

// #Medium #Array #Two_Pointers #Heap_Priority_Queue #Simulation #LeetCode_75_Heap/Priority_Queue
// #2023_01_07_Time_57_ms_(96.24%)_Space_54_MB_(92.26%)
// #2026_10_22_Time_26_ms_(99.83%)_Space_78.37_MB_(36.12%)

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

public class Solution {
public long totalCost(int[] costs, int k, int candidates) {
// Hint: Maintain two minheaps, one for the left end and one for the right end
// This problem is intentionally made complex but actually we don't have to record the
// indices
int n = costs.length;
PriorityQueue<Integer> leftMinHeap = new PriorityQueue<>();
PriorityQueue<Integer> rightMinHeap = new PriorityQueue<>();
long res = 0;
if (2 * candidates >= n) {
for (int i = 0; i <= n / 2; i++) {
leftMinHeap.add(costs[i]);
}
for (int i = n / 2 + 1; i < n; i++) {
rightMinHeap.add(costs[i]);
}
while (!leftMinHeap.isEmpty() && !rightMinHeap.isEmpty() && k > 0) {
if (leftMinHeap.peek() <= rightMinHeap.peek()) {
res += leftMinHeap.poll();
} else {
res += rightMinHeap.poll();
}
k -= 1;
}
} else {
int left = candidates;
int right = n - candidates - 1;
for (int i = 0; i < candidates; i++) {
leftMinHeap.add(costs[i]);
}
for (int i = n - candidates; i < n; i++) {
rightMinHeap.add(costs[i]);
}
while (!leftMinHeap.isEmpty() && !rightMinHeap.isEmpty() && k > 0) {
if (leftMinHeap.peek() <= rightMinHeap.peek()) {
res += leftMinHeap.poll();
if (left <= right) {
leftMinHeap.add(costs[left]);
}
left += 1;
} else {
res += rightMinHeap.poll();
if (right >= left) {
rightMinHeap.add(costs[right]);
}
right -= 1;
}
k -= 1;
if (candidates * 2 + k > n) {
Arrays.sort(costs);
for (int i = 0; i < k; i++) {
res += costs[i];
}
return res;
}
if (k > 0 && leftMinHeap.isEmpty()) {
while (k > 0) {
res += rightMinHeap.poll();
k -= 1;
}
PriorityQueue<Integer> pre = new PriorityQueue<>();
PriorityQueue<Integer> suf = new PriorityQueue<>();
for (int i = 0; i < candidates; i++) {
pre.offer(costs[i]);
suf.offer(costs[n - 1 - i]);
}
if (k > 0 && rightMinHeap.isEmpty()) {
while (k > 0) {
res += leftMinHeap.poll();
k -= 1;
int i = candidates;
int j = n - 1 - candidates;
while (k-- > 0) {
if (pre.peek() <= suf.peek()) {
res += pre.poll();
pre.offer(costs[i++]);
} else {
res += suf.poll();
suf.offer(costs[j--]);
}
}
return res;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,55 @@ void totalCost() {
void totalCost2() {
assertThat(new Solution().totalCost(new int[] {1, 2, 4, 1}, 3, 3), equalTo(4L));
}

@Test
void totalCost3() {
// Hire everyone; sorting branch
assertThat(new Solution().totalCost(new int[] {1, 2, 3, 4, 5}, 5, 1), equalTo(15L));
}

@Test
void totalCost4() {
// Single hire, suffix candidate is cheaper
assertThat(new Solution().totalCost(new int[] {5, 1, 3}, 1, 1), equalTo(3L));
}

@Test
void totalCost5() {
// Tie between prefix and suffix, prefix (lower index) is chosen
assertThat(new Solution().totalCost(new int[] {3, 1, 3}, 1, 1), equalTo(3L));
}

@Test
void totalCost6() {
// Candidates cover the whole array
assertThat(new Solution().totalCost(new int[] {5, 4, 3, 2, 1}, 2, 5), equalTo(3L));
}

@Test
void totalCost7() {
// Heap path, picks from both sides
assertThat(
new Solution().totalCost(new int[] {10, 1, 10, 1, 10, 1, 10}, 2, 2), equalTo(2L));
}

@Test
void totalCost8() {
// Heap path, longer simulation mixing prefix and suffix picks
assertThat(
new Solution()
.totalCost(
new int[] {
31, 25, 72, 79, 74, 65, 84, 91, 18, 59, 27, 9, 81, 33, 17, 58
},
11,
2),
equalTo(423L));
}

@Test
void totalCost9() {
// Single element
assertThat(new Solution().totalCost(new int[] {7}, 1, 1), equalTo(7L));
}
}
Loading