Topics › Greedy Algorithms › Easy Problems
Assign Cookies
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You have some children and some cookies. Child i has a greed factor greed[i]: the smallest cookie size that will make that child content. Cookie j has size cookies[j]. Each child can receive at most one cookie and each cookie can go to at most one child. A child is content when the cookie they receive has size >= greed[i]. Return the maximum number of content children.
Constraints: 0 ≤ n, m ≤ 3 × 10^4 (children and cookies), and every greed factor and cookie size is between 1 and 2^31 - 1. The algorithms only compare values, never add them, so overflow is not a concern.
Example 1
greed = [1, 2, 3], cookies = [1, 1]
answer = 1 // both cookies have size 1, only the child with greed 1 can be satisfied
Example 2
greed = [1, 2], cookies = [1, 2, 3]
answer = 2 // give size 1 to greed 1 and size 2 (or 3) to greed 2
The greedy insight
Before writing any code it helps to see why a simple rule is safe: satisfy the least greedy child first, using the smallest cookie that is big enough.
- Least greedy first. If some set of
kchildren can be made content, then thekleast greedy children can be made content too: hand each of them the cookie that went to a greedier child in that set. So we lose nothing by always serving the easiest child. - Smallest sufficient cookie. Suppose an optimal assignment gives the least greedy child
ca cookie larger than the smallest cookiexthat fitsc. Swap: givextoc, and give the larger cookie to whoever heldx(if anyone). That person is still content because their new cookie is even bigger. The count does not change, so some optimal answer matchescwithx.
Once c and x are matched, the rest is the same problem on fewer children and cookies, so applying the rule repeatedly is optimal. The two approaches below both follow this rule; they differ only in how fast they find “the smallest cookie that fits”.
Approach 1: Brute force, linear search for every child
Intuition. Sort the children by greed so we serve them easiest first. For each child, look through all cookies and pick the smallest unused one that is large enough. If no cookie fits this child, no cookie fits any later child either, because later children are greedier, so we can stop.
Algorithm.
- Copy and sort
greedin ascending order. Keep ausedflag per cookie. - For each greed value
needin that order:- Scan every cookie and remember the index of the smallest unused cookie with size
>= need. - If none exists, stop. Otherwise mark it used and count one more content child.
- Scan every cookie and remember the index of the smallest unused cookie with size
- Return the count.
static int bruteForce(int[] greed, int[] cookies) {
int[] g = greed.clone();
Arrays.sort(g);
boolean[] used = new boolean[cookies.length];
int content = 0;
for (int need : g) {
int pick = -1;
for (int j = 0; j < cookies.length; j++) {
if (!used[j] && cookies[j] >= need && (pick == -1 || cookies[j] < cookies[pick])) pick = j;
}
if (pick == -1) break; // nothing fits this child, so nothing fits a greedier one
used[pick] = true;
content++;
}
return content;
}
Complexity. Time O(n log n + n·m): sorting the children, then a full scan of the m cookies for each of the n children. Space O(n + m) for the sorted copy and the used flags.
With 3 × 10^4 children and cookies this is about 9 × 10^8 comparisons. It works, but it repeats the same search over and over. The waste is obvious: once cookies are sorted, the “smallest unused cookie that fits” only ever moves forward.
Approach 2: Optimal, sort both and use two pointers
Intuition. Sort both arrays. Keep a pointer child at the least greedy child still waiting and a pointer cookie at the smallest cookie not yet considered. Look at the current cookie:
- If it satisfies the current child, give it to them and move on to the next child.
- If it does not, this cookie is too small for the least greedy waiting child, hence too small for every waiting child. Throw it away.
Either way the cookie is finished with, so cookie always advances. Because children are served in increasing greed and cookies are tried in increasing size, the first cookie that fits a child is exactly the smallest sufficient one from the exchange argument above.
Algorithm.
- Copy and sort both arrays. Set
child = 0,cookie = 0. - While both pointers are in range:
- If
s[cookie] >= g[child], dochild++. - Do
cookie++.
- If
- Return
child, which equals the number of satisfied children.
static int optimal(int[] greed, int[] cookies) {
int[] g = greed.clone(), s = cookies.clone();
Arrays.sort(g);
Arrays.sort(s);
int child = 0, cookie = 0;
while (child < g.length && cookie < s.length) {
if (s[cookie] >= g[child]) child++; // smallest remaining cookie satisfies the least greedy waiting child
cookie++; // the cookie is either given away or too small for everyone left
}
return child;
}
Dry run on greed = [10, 9, 8, 7], cookies = [5, 6, 7, 8]. After sorting, g = [7, 8, 9, 10] and s = [5, 6, 7, 8].
| cookie | s[cookie] | child | g[child] | fits? | action |
|---|---|---|---|---|---|
| 0 | 5 | 0 | 7 | no | discard cookie |
| 1 | 6 | 0 | 7 | no | discard cookie |
| 2 | 7 | 0 | 7 | yes | child becomes 1 |
| 3 | 8 | 1 | 8 | yes | child becomes 2 |
Cookies run out, so the answer is 2: the children with greed 7 and 8 are content.
Complexity. Time O(n log n + m log m) for the two sorts; the sweep itself is O(n + m) because each step advances cookie. Space O(n + m) for the copies. If you are allowed to sort the input arrays in place, the extra space drops to the sort’s own stack.
The method sorts copies so the caller’s arrays keep their order. In an interview you can sort in place, but saying so explicitly shows you noticed the side effect.
Edge cases and pitfalls
- No children or no cookies. The
whilecondition fails immediately and the answer is0. Both cases are in the tests. - Duplicates. Several children with the same greed, or several equal cookies, need no special handling; each cookie is used at most once because the pointer moves past it.
- One big cookie.
greed = [1, 2, 3],cookies = [3]gives1, not3: a cookie is never split. - Unsorted input. The examples on most judges look sorted, but the input is not guaranteed to be. Forgetting to sort is the most common wrong answer.
- Wrong greedy order. Giving the largest cookie to the least greedy child wastes capacity. The rule is smallest sufficient cookie, not any cookie that fits. Serving the greediest child first with the largest cookie is also correct, but mixing the two directions is not.
- Mutating inputs. Sorting in place changes the caller’s arrays; the Java file checks that
optimalleaves its input untouched.
Related problems
- Lemonade Change is another greedy problem where you hand out the “least valuable” resource first.
- Fractional Knapsack uses the same pattern of sorting and then consuming resources in a fixed order.
- N meetings in one room proves its greedy choice with the same exchange argument.
- Candy is a harder greedy problem about distributing items to children.