Problem

You have n boxes labeled from 0 to n - 1. You are given four arrays: statuscandieskeys, and containedBoxes where:

  • status[i] is 1 if the ith box is open and 0 if the ith box is closed,
  • candies[i] is the number of candies in the ith box,
  • keys[i] is a list of the labels of the boxes you can open after opening the ith box.
  • containedBoxes[i] is a list of the boxes you found inside the ith box.

You are given an integer array initialBoxes that contains the labels of the boxes you initially have. You can take all the candies in any open box and you can use the keys in it to open new boxes and you also can use the boxes you find in it.

Return the maximum number of candies you can get following the rules above.

Examples

Example 1:

1
2
3
4
5
6
Input: status = [1,0,1,0], candies = [7,5,4,100], keys = [[],[],[1],[]], containedBoxes = [[1,2],[3],[],[]], initialBoxes = [0]
Output: 16
Explanation: You will be initially given box 0. You will find 7 candies in it and boxes 1 and 2.
Box 1 is closed and you do not have a key for it so you will open box 2. You will find 4 candies and a key to box 1 in box 2.
In box 1, you will find 5 candies and box 3 but you will not find a key to box 3 so box 3 will remain closed.
Total number of candies collected = 7 + 4 + 5 = 16 candy.

Example 2:

1
2
3
4
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
Explanation: You have initially box 0. Opening it you can find boxes 1,2,3,4 and 5 and their keys.
The total number of candies will be 6.

Constraints:

  • n == status.length == candies.length == keys.length == containedBoxes.length
  • 1 <= n <= 1000
  • status[i] is either 0 or 1.
  • 1 <= candies[i] <= 1000
  • 0 <= keys[i].length <= n
  • 0 <= keys[i][j] < n
  • All values of keys[i] are unique.
  • 0 <= containedBoxes[i].length <= n
  • 0 <= containedBoxes[i][j] < n
  • All values of containedBoxes[i] are unique.
  • Each box is contained in one box at most.
  • 0 <= initialBoxes.length <= n
  • 0 <= initialBoxes[i] < n

Solution

Method 1 – BFS with State Tracking

Intuition

The problem is about collecting candies by opening boxes, where some boxes are locked and can only be opened if you have their keys. The process is similar to traversing a graph where boxes are nodes, and you can only visit (open) a node if you have its key or it’s already open. Using BFS ensures we process boxes as soon as they’re available, and we keep track of keys and boxes we find along the way.

Approach

  1. Initialization:
  • Use a queue to process boxes you can open.
  • Use sets to track boxes you have, keys you have, and boxes already opened.
  1. Processing:
  • For each box in the queue:
    • If it’s already opened, skip.
    • If it’s closed and you don’t have the key, skip for now.
    • If it’s open or you have the key:
    • Collect candies.
    • Mark as opened.
    • Add any new keys found to your key set.
    • Add any new boxes found to your box set and queue.
    • If you now have keys for boxes you couldn’t open before, try them again.
  1. Repeat until no more boxes can be opened.
  2. Return the total candies collected.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public:
   int maxCandies(vector<int>& status, vector<int>& candies, vector<vector<int>>& keys, vector<vector<int>>& containedBoxes, vector<int>& initialBoxes) {
      int n = status.size(), ans = 0;
      vector<bool> box(n, false), opened(n, false), haveKey(n, false);
      queue<int> q;
      for (int b : initialBoxes) {
        box[b] = true;
        q.push(b);
      }
      while (!q.empty()) {
        int sz = q.size();
        bool progress = false;
        for (int i = 0; i < sz; ++i) {
           int b = q.front(); q.pop();
           if (opened[b]) continue;
           if (!status[b] && !haveKey[b]) {
              q.push(b);
              continue;
           }
           ans += candies[b];
           opened[b] = true;
           for (int k : keys[b]) {
              haveKey[k] = true;
              if (box[k] && !opened[k]) q.push(k);
           }
           for (int c : containedBoxes[b]) {
              box[c] = true;
              if (!opened[c]) q.push(c);
           }
           progress = true;
        }
        if (!progress) break;
      }
      return ans;
   }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
   public int maxCandies(int[] status, int[] candies, int[][] keys, int[][] containedBoxes, int[] initialBoxes) {
      int n = status.length, ans = 0;
      boolean[] box = new boolean[n], opened = new boolean[n], haveKey = new boolean[n];
      Queue<Integer> q = new LinkedList<>();
      for (int b : initialBoxes) {
        box[b] = true;
        q.offer(b);
      }
      while (!q.isEmpty()) {
        int sz = q.size();
        boolean progress = false;
        for (int i = 0; i < sz; ++i) {
           int b = q.poll();
           if (opened[b]) continue;
           if (status[b] == 0 && !haveKey[b]) {
              q.offer(b);
              continue;
           }
           ans += candies[b];
           opened[b] = true;
           for (int k : keys[b]) {
              haveKey[k] = true;
              if (box[k] && !opened[k]) q.offer(k);
           }
           for (int c : containedBoxes[b]) {
              box[c] = true;
              if (!opened[c]) q.offer(c);
           }
           progress = true;
        }
        if (!progress) break;
      }
      return ans;
   }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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)
      ans = 0
      box = [False] * n
      opened = [False] * n
      have_key = [False] * n
      from collections import deque
      q = deque()
      for b in initialBoxes:
        box[b] = True
        q.append(b)
      while q:
        sz = len(q)
        progress = False
        for _ in range(sz):
           b = q.popleft()
           if opened[b]:
              continue
           if status[b] == 0 and not have_key[b]:
              q.append(b)
              continue
           ans += candies[b]
           opened[b] = True
           for k in keys[b]:
              have_key[k] = True
              if box[k] and not opened[k]:
                q.append(k)
           for c in containedBoxes[b]:
              box[c] = True
              if not opened[c]:
                q.append(c)
           progress = True
        if not progress:
           break
      return ans

Complexity

  • ⏰ Time complexity: O(N + total_keys + total_containedBoxes) — Each box, key, and contained box is processed at most once.
  • 🧺 Space complexity: O(N) — For tracking box states, keys, and queue.