Maximum Candies You Can Get from Boxes — Problem & Solution
Maximum Candies You Can Get from Boxes solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A hard problem.
Maximum Candies You Can Get from Boxes is a hard coding problem on arrays, breadth-first search and graph, asked in Amazon and Airbnb interviews.
- Difficulty: Hard
- Topics: Arrays, Breadth-First Search, Graph
- Asked at: Amazon, Airbnb
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
There are n boxes labelled 0 to n - 1. For each box i:
status[i]is1if the box is open and0if it is closed;candies[i]is the number of candies inside;keys[i]lists the boxes whose keys are inside boxi;containedBoxes[i]lists the boxes found inside boxi.
You start holding the boxes in initialBoxes. You can take the candies from any box you hold that is open, and use the keys and boxes inside it. A key opens its box whenever you come to hold that box (or immediately, if you already hold it).
Return the maximum number of candies you can collect.
Example 1
Input: status = [1,0,1,0], candies = [7,5,4,100], keys = [[],[],[1],[]], containedBoxes = [[1,2],[3],[],[]], initialBoxes = [0]
Output: 16
Explanation: Box 0 gives 7 candies and boxes 1 and 2. Box 2 is open: 4 candies and the key to box 1. Box 1 gives 5 candies and box 3, which stays locked. 7 + 4 + 5 = 16.
Example 2
Input: status = [1,0,0,0,0,0], candies = [1,1,1,1,1,1], keys = [[1,2,3,4,5],[],[],[],[],[]], containedBoxes = [[1,2,3,4,5],[],[],[],[],[]], initialBoxes = [0]
Output: 6
Example 3
Input: status = [0], candies = [5], keys = [[]], containedBoxes = [[]], initialBoxes = [0]
Output: 0
Explanation: The only box is locked and its key is nowhere.
Constraints
n == status.length == candies.length == keys.length == containedBoxes.length1 <= n <= 1000status[i] is 0 or 11 <= candies[i] <= 10000 <= keys[i].length <= n0 <= keys[i][j] < n, and the values of keys[i] are unique0 <= containedBoxes[i].length <= n0 <= containedBoxes[i][j] < n, and the values of containedBoxes[i] are uniqueeach box is contained in at most one box0 <= initialBoxes.length <= n0 <= initialBoxes[i] < n
Hints for Maximum Candies You Can Get from Boxes
Hint 1
A box can be opened exactly when you hold it and it is open or you have its key. Both facts only ever become true, never false.
Hint 2
Keep a queue of boxes ready to open. Whenever a box is opened, it may make other boxes ready: through a key it contains, or through a box it contains.
Hint 3
Mark a box as queued the moment it becomes ready so it is opened once. Sum the candies of every box you open.
How to solve Maximum Candies You Can Get from Boxes
Opening a box only ever adds keys and boxes, so there is no choice to make — open everything that can ever be opened. A breadth-first search over "ready" boxes does this in linear time.
Approach
- Track
held[i],canOpen[i](initiallystatus[i]) andqueued[i]. - Mark every initial box as held; queue those that can be opened.
- Pop a box, add its candies. For every key
kinside: setcanOpen[k], and queuekif it is held and not yet queued. For every boxcinside: setheld[c], and queue it if it can be opened and is not yet queued. - Return the total when the queue empties.
Why it works
Holding a box and being able to open it are monotone facts, so opening a box early never prevents anything later. The queue opens a box as soon as both facts hold, and each event that could complete them (finding a key, finding a box) re-checks the affected box. Therefore every box that any strategy could open is opened, exactly once.
Complexity
- Time —
O(n + total keys + total contained boxes) - Space —
O(n)
Pitfalls
- A key found for a box you do not hold yet must be remembered — the box may turn up later.
- A box can become ready through a key or through being found; check readiness in both places.
- Use a
queuedflag so a box is never counted twice.
Reference solution
Python
from collections import deque
from typing import List
def maxCandies(status: List[int], candies: List[int], keys: List[List[int]],
containedBoxes: List[List[int]], initialBoxes: List[int]) -> int:
n = len(status)
can_open = status[:]
held = [False] * n
queued = [False] * n
q = deque()
for b in initialBoxes:
held[b] = True
if can_open[b] and not queued[b]:
queued[b] = True
q.append(b)
total = 0
while q:
b = q.popleft()
total += candies[b]
for k in keys[b]:
can_open[k] = 1
if held[k] and not queued[k]:
queued[k] = True
q.append(k)
for c in containedBoxes[b]:
held[c] = True
if can_open[c] and not queued[c]:
queued[c] = True
q.append(c)
return totalJava
public static int maxCandies(int[] status, int[] candies, int[][] keys, int[][] containedBoxes, int[] initialBoxes) {
int n = status.length, total = 0, head = 0, tail = 0;
int[] canOpen = status.clone(), q = new int[n];
boolean[] held = new boolean[n], queued = new boolean[n];
for (int b : initialBoxes) {
held[b] = true;
if (canOpen[b] == 1 && !queued[b]) { queued[b] = true; q[tail++] = b; }
}
while (head < tail) {
int b = q[head++];
total += candies[b];
for (int k : keys[b]) {
canOpen[k] = 1;
if (held[k] && !queued[k]) { queued[k] = true; q[tail++] = k; }
}
for (int c : containedBoxes[b]) {
held[c] = true;
if (canOpen[c] == 1 && !queued[c]) { queued[c] = true; q[tail++] = c; }
}
}
return total;
}C++
int maxCandies(vector<int>& status, vector<int>& candies, vector<vector<int>>& keys, vector<vector<int>>& containedBoxes, vector<int>& initialBoxes) {
int n = status.size(), total = 0;
vector<int> canOpen = status;
vector<bool> held(n, false), queued(n, false);
queue<int> q;
for (int b : initialBoxes) {
held[b] = true;
if (canOpen[b] && !queued[b]) { queued[b] = true; q.push(b); }
}
while (!q.empty()) {
int b = q.front(); q.pop();
total += candies[b];
for (int k : keys[b]) {
canOpen[k] = 1;
if (held[k] && !queued[k]) { queued[k] = true; q.push(k); }
}
for (int c : containedBoxes[b]) {
held[c] = true;
if (canOpen[c] && !queued[c]) { queued[c] = true; q.push(c); }
}
}
return total;
}JavaScript
var maxCandies = function(status, candies, keys, containedBoxes, initialBoxes) {
var n = status.length, canOpen = status.slice(), held = [], queued = [], q = [], total = 0;
for (var i = 0; i < n; i++) { held.push(false); queued.push(false); }
for (var a = 0; a < initialBoxes.length; a++) {
var b0 = initialBoxes[a];
held[b0] = true;
if (canOpen[b0] && !queued[b0]) { queued[b0] = true; q.push(b0); }
}
for (var h = 0; h < q.length; h++) {
var b = q[h];
total += candies[b];
for (var x = 0; x < keys[b].length; x++) {
var k = keys[b][x];
canOpen[k] = 1;
if (held[k] && !queued[k]) { queued[k] = true; q.push(k); }
}
for (var y = 0; y < containedBoxes[b].length; y++) {
var c = containedBoxes[b][y];
held[c] = true;
if (canOpen[c] && !queued[c]) { queued[c] = true; q.push(c); }
}
}
return total;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 1199 arrays problems · the whole catalogue
Learn the technique: Arrays · Breadth-First Search (BFS)