Problem of The Day: Maximum Candies You Can Get from Boxes
Problem Statement
Intuition
The problem involves navigating through a system of boxes that may be locked or unlocked, each potentially containing candies, keys to unlock other boxes, or additional boxes. The goal is to collect the maximum number of candies possible. This setup naturally leads to a simulation using a graph-like traversal while managing access via keys and status.
Approach
We simulate the process using a priority queue (heapq
) to manage the order of boxes to process, prioritizing unlocked boxes. Here’s the step-by-step:
- Create a mapping from each box to its contained boxes (
graph
). - Initialize a max-heap with all
initialBoxes
, storing them as(-status[box], box)
so that unlocked boxes (status = 1) are prioritized. - Maintain a
visited
set to ensure each box is processed only once. - While the heap is not empty:
- Pop the box with the highest priority.
- If it is accessible (
status[box] == 1
) and not yet visited:- Mark it as visited.
- Add its candies to the result.
- For each key in this box, update the status of the corresponding box to unlocked.
- For each contained box, push it into the heap with its current status.
This ensures all accessible paths are explored, keys are propagated properly, and boxes are not reprocessed.
Complexity
-
Time complexity:
\(O(n \cdot \log n + k)\)
Where (n) is the number of boxes and (k) is the total number of keys and contained boxes. Each box is pushed and popped at most once due to thevisited
check. -
Space complexity:
\(O(n + k)\)
For thevisited
set, heap, and auxiliary structures like the graph and key/box lists.
Code
import heapq
class Solution:
def maxCandies(self, status: List[int], candies: List[int], keys: List[List[int]], containedBoxes: List[List[int]], initialBoxes: List[int]) -> int:
graph = {i: box for i, box in enumerate(containedBoxes)}
max_heap = []
for initialBox in initialBoxes:
heapq.heappush(max_heap, [-1 * status[initialBox], initialBox])
res = 0
visited = set()
while max_heap:
s, box_label = heapq.heappop(max_heap)
s *= -1
if status[box_label] == 1 and box_label not in visited:
visited.add(box_label)
res += candies[box_label]
for key in keys[box_label]:
status[key] = 1
for nei in graph[box_label]:
heapq.heappush(max_heap, [-1 * status[nei], nei])
return res
Editorial
Approach: Breadth-First Search
class Solution:
def maxCandies(
self,
status: List[int],
candies: List[int],
keys: List[List[int]],
containedBoxes: List[List[int]],
initialBoxes: List[int],
) -> int:
n = len(status)
can_open = [status[i] == 1 for i in range(n)]
has_box, used = [False] * n, [False] * n
q = collections.deque()
ans = 0
for box in initialBoxes:
has_box[box] = True
if can_open[box]:
q.append(box)
used[box] = True
ans += candies[box]
while len(q) > 0:
big_box = q.popleft()
for key in keys[big_box]:
can_open[key] = True
if not used[key] and has_box[key]:
q.append(key)
used[key] = True
ans += candies[key]
for box in containedBoxes[big_box]:
has_box[box] = True
if not used[box] and can_open[box]:
q.append(box)
used[box] = True
ans += candies[box]
return ans