Greedy · 24 of 35
Chocolate Distribution Problem
Give one chocolate packet to each kid so the biggest and smallest packets handed out are as close as possible.
- Simple way
- time O(C(n, m) * m)
room O(m) - Clever way
- time O(n log n)
room O(1)
The problem, simply
You get n packets of chocolate. Packet i holds arr[i] pieces. There are m kids, and m is at most n. Every kid gets exactly one packet.
Choose the m packets so that the difference between the fullest packet and the emptiest packet you give out is as small as possible. Give back that difference.
A story to remember it
Picture it
Eight packets as they come. There are 5 kids. Packet 56 is huge and packet 1 is tiny.
- kids
- 5
Examples
A normal case
- In
arr = [3, 4, 1, 9, 56, 7, 9, 12], m = 5- Out
6
Give out 3, 4, 7, 9 and 9. The gap is 9 - 3 = 6.
A small group
- In
arr = [7, 3, 2, 4, 9, 12, 56], m = 3- Out
2
Give out 2, 3 and 4. The gap is 2.
One kid
- In
arr = [5, 8, 1], m = 1- Out
0
One packet has nothing to be compared with.
As many kids as packets
- In
arr = [10, 2, 7], m = 3- Out
8
Every packet is used, so the gap is the biggest minus the smallest, 10 - 2.
Way 1: the simple way
Try every group of m packets. For each group, find its biggest and smallest packet. Keep the smallest gap.
- Pick packets one by one, choosing "take" or "skip" for each, until
mare taken. - For a full group, compute
max - min. - Keep the smallest result.
class Solution {
private int best;
public int findMinDiff(int[] arr, int m) {
if (m == 0 || arr.length == 0) return 0;
best = Integer.MAX_VALUE;
choose(arr, 0, m, new ArrayList<>());
return best;
}
private void choose(int[] arr, int i, int m, List<Integer> group) {
if (group.size() == m) {
best = Math.min(best, Collections.max(group) - Collections.min(group));
return;
}
if (arr.length - i < m - group.size()) return; // not enough packets left
group.add(arr[i]); // take packet i
choose(arr, i + 1, m, group);
group.remove(group.size() - 1);
choose(arr, i + 1, m, group); // skip packet i
}
}How long it takes: there are C(n, m) groups, each checked in O(m), O(C(n, m) * m). Extra room: the current group, O(m).
Way 2: the clever way
Sort the packets. If you skip a packet in the middle of your group, you could swap it in for the biggest or the smallest one and the gap would not grow. So the best group is always m packets in a row in the sorted line.
- Sort
arr. - Slide a window of
mpackets:arr[i]toarr[i + m - 1]. - The gap of each window is
arr[i + m - 1] - arr[i]. Keep the smallest.
Slide a frame of 5 over the sorted packets
01 / 06
Line up the packets from smallest to biggest. Now any 5 packets that are close in size sit next to each other.
- kids
- 5
- best
- none
- checking
- answer
class Solution {
public int findMinDiff(int[] arr, int m) {
int n = arr.length;
if (m == 0 || n == 0) return 0;
Arrays.sort(arr); // close sizes now sit side by side
int best = Integer.MAX_VALUE;
for (int i = 0; i + m - 1 < n; i++) {
int gap = arr[i + m - 1] - arr[i]; // biggest minus smallest in this window
best = Math.min(best, gap);
}
return best;
}
}How long it takes: the sort is O(n log n) and the slide is O(n), so O(n log n). Extra room: O(1) besides what the sort uses.
Watch out for
- Off by one in the loop end. The last window starts at
n - m, so the loop runs whilei + m - 1 < n. mbigger thannhas no answer. Decide what to return (many judges say it never happens).m = 0or an empty array: return 0.- Sorting changes the caller's array. Copy it first if that matters.
What interviewers ask next
- Why must the best group be in a row after sorting? If a packet between the smallest and biggest chosen ones is left out, swapping it for an end packet never makes the gap bigger. Repeat until the group has no holes.
- What if the packets cannot be reordered and the kids must take
mpackets that sit next to each other on the shelf? Slide the window over the original order and keep the window's smallest and biggest with two double ended queues. That isO(n). - Minimum difference between the highest and lowest of
kscores (LeetCode 1984) is exactly this problem. - Can you avoid the full sort? If values are small, counting sort makes it
O(n + maxValue).
Remember this
Grown-up words: the frame over the bags is a fixed size sliding window, lining up the bags is sorting, and "no holes in the best group" is the reason the window is enough.