Skip to content

Commit 54c5750

Browse files
authored
feat: add solutions to lc problem: No.0865 (doocs#3762)
No.0865.Smallest Subtree with all the Deepest Nodes
1 parent e78da54 commit 54c5750

File tree

9 files changed

+337
-173
lines changed

9 files changed

+337
-173
lines changed

solution/0800-0899/0864.Shortest Path to Get All Keys/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,13 @@ tags:
9898
f d c b
9999
```
100100

101-
我们定义一个队列 $q$ 来存储当前位置以及当前拥有的钥匙的状态,即 $(i, j, state)$,其中 $(i, j)$ 表示当前位置,$state$ 表示当前拥有的钥匙的状态,即 $state$ 的第 $i$ 位为 $1$ 表示当前拥有第 $i$ 把钥匙,否则表示当前没有第 $i$ 把钥匙。
101+
我们定义一个队列 $q$ 来存储当前位置以及当前拥有的钥匙的状态,即 $(i, j, \textit{state})$,其中 $(i, j)$ 表示当前位置,$\textit{state}$ 表示当前拥有的钥匙的状态,即 $\textit{state}$ 的第 $i$ 位为 $1$ 表示当前拥有第 $i$ 把钥匙,否则表示当前没有第 $i$ 把钥匙。
102102

103-
另外,定义哈希表或数组 $vis$ 记录当前位置以及当前拥有的钥匙的状态是否已经被访问过,如果访问过,则不需要再次访问。$vis[i][j][state]$ 表示当前位置为 $(i, j)$,当前拥有的钥匙的状态为 $state$ 时,是否已经被访问过。
103+
另外,定义哈希表或数组 $vis$ 记录当前位置以及当前拥有的钥匙的状态是否已经被访问过,如果访问过,则不需要再次访问。$vis[i][j][\textit{state}]$ 表示当前位置为 $(i, j)$,当前拥有的钥匙的状态为 $state$ 时,是否已经被访问过。
104104

105105
我们从起点 $(si, sj)$ 出发,将其加入队列 $q$,并将 $vis[si][sj][0]$ 置为 $true$,表示起点位置以及拥有的钥匙的状态为 $0$ 时已经被访问过。
106106

107-
在广度优先搜索的过程中,我们每次从队首取出一个位置 $(i, j, state)$,并判断当前位置是否为终点,即当前位置是否拥有所有的钥匙,即 $state$ 的二进制表示中的 $1$ 的个数是否为 $k$。如果是,将当前步数作为答案返回。
107+
在广度优先搜索的过程中,我们每次从队首取出一个位置 $(i, j, \textit{state})$,并判断当前位置是否为终点,即当前位置是否拥有所有的钥匙,即 $state$ 的二进制表示中的 $1$ 的个数是否为 $k$。如果是,将当前步数作为答案返回。
108108

109109
否则,我们从当前位置出发,往上下左右四个方向走,如果可以走到下一个位置 $(x, y)$,则将 $(x, y, nxt)$ 加入队列 $q$,其中 $nxt$ 表示下一个位置的钥匙的状态。
110110

solution/0800-0899/0864.Shortest Path to Get All Keys/README_EN.md

+69-39
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,37 @@ tags:
8080

8181
<!-- solution:start -->
8282

83-
### Solution 1
83+
### Solution 1: State Compression + BFS
84+
85+
According to the problem description, we need to start from the initial position, move in four directions (up, down, left, right), collect all keys, and finally return the minimum number of moves required to collect all keys. If it is not possible to collect all keys, return $-1$.
86+
87+
First, we traverse the 2D grid to find the starting position $(si, sj)$ and count the number of keys $k$.
88+
89+
Then, we can use Breadth-First Search (BFS) to solve this problem. Since the number of keys ranges from $1$ to $6$, we can use a binary number to represent the state of the keys, where the $i$-th bit being $1$ indicates that the $i$-th key has been collected, and $0$ indicates that the $i$-th key has not been collected.
90+
91+
For example, in the following case, there are $4$ bits set to $1$, indicating that keys `'b', 'c', 'd', 'f'` have been collected.
92+
93+
```
94+
1 0 1 1 1 0
95+
^ ^ ^ ^
96+
f d c b
97+
```
98+
99+
We define a queue $q$ to store the current position and the state of the collected keys, i.e., $(i, j, \textit{state})$, where $(i, j)$ represents the current position, and $\textit{state}$ represents the state of the collected keys. The $i$-th bit of $\textit{state}$ being $1$ indicates that the $i$-th key has been collected; otherwise, it indicates that the $i$-th key has not been collected.
100+
101+
Additionally, we define a hash table or array $vis$ to record whether the current position and the state of the collected keys have been visited. If visited, there is no need to visit again. $vis[i][j][\textit{state}]$ indicates whether the position $(i, j)$ and the state of the collected keys $state$ have been visited.
102+
103+
We start from the initial position $(si, sj)$, add it to the queue $q$, and set $vis[si][sj][0]$ to $true$, indicating that the initial position and the state of the collected keys $0$ have been visited.
104+
105+
During the BFS process, we take out a position $(i, j, \textit{state})$ from the front of the queue and check whether the current position is the endpoint, i.e., whether the current position has collected all keys, which means the number of $1$s in the binary representation of $state$ is $k$. If so, we return the current number of steps as the answer.
106+
107+
Otherwise, we move from the current position in four directions (up, down, left, right). If we can move to the next position $(x, y)$, we add $(x, y, nxt)$ to the queue $q$, where $nxt$ represents the state of the keys at the next position.
108+
109+
Here, $(x, y)$ must first be within the grid range, i.e., $0 \leq x < m$ and $0 \leq y < n$. Secondly, if the position $(x, y)$ is a wall, i.e., `grid[x][y] == '#'`, or the position $(x, y)$ is a lock but we do not have the corresponding key, i.e., `grid[x][y] >= 'A' && grid[x][y] <= 'F' && (state >> (grid[x][y] - 'A') & 1) == 0)`, then we cannot move to the position $(x, y)`. Otherwise, we can move to the position $(x, y)`.
110+
111+
If the search ends and we have not collected all keys, return $-1$.
112+
113+
The time complexity is $O(m \times n \times 2^k)$, and the space complexity is $O(m \times n \times 2^k)$. Here, $m$ and $n$ are the number of rows and columns of the grid, respectively, and $k$ is the number of keys.
84114

85115
<!-- tabs:start -->
86116

@@ -90,9 +120,9 @@ tags:
90120
class Solution:
91121
def shortestPathAllKeys(self, grid: List[str]) -> int:
92122
m, n = len(grid), len(grid[0])
93-
# 找起点 (si, sj)
123+
# Find the starting point (si, sj)
94124
si, sj = next((i, j) for i in range(m) for j in range(n) if grid[i][j] == '@')
95-
# 统计钥匙数量
125+
# Count the number of keys
96126
k = sum(v.islower() for row in grid for v in row)
97127
dirs = (-1, 0, 1, 0, -1)
98128
q = deque([(si, sj, 0)])
@@ -101,33 +131,33 @@ class Solution:
101131
while q:
102132
for _ in range(len(q)):
103133
i, j, state = q.popleft()
104-
# 找到所有钥匙,返回当前步数
134+
# If all keys are found, return the current step count
105135
if state == (1 << k) - 1:
106136
return ans
107137

108-
# 往四个方向搜索
138+
# Search in the four directions
109139
for a, b in pairwise(dirs):
110140
x, y = i + a, j + b
111141
nxt = state
112-
# 在边界范围内
142+
# Within boundary limits
113143
if 0 <= x < m and 0 <= y < n:
114144
c = grid[x][y]
115-
# 是墙,或者是锁,但此时没有对应的钥匙,无法通过
145+
# It's a wall, or it's a lock but we don't have the key for it
116146
if (
117147
c == '#'
118148
or c.isupper()
119149
and (state & (1 << (ord(c) - ord('A')))) == 0
120150
):
121151
continue
122-
# 是钥匙
152+
# It's a key
123153
if c.islower():
124-
# 更新状态
154+
# Update the state
125155
nxt |= 1 << (ord(c) - ord('a'))
126-
# 此状态未访问过,入队
156+
# If this state has not been visited, enqueue it
127157
if (x, y, nxt) not in vis:
128158
vis.add((x, y, nxt))
129159
q.append((x, y, nxt))
130-
# 步数加一
160+
# Increment the step count
131161
ans += 1
132162
return -1
133163
```
@@ -146,10 +176,10 @@ class Solution {
146176
for (int j = 0; j < n; ++j) {
147177
char c = grid[i].charAt(j);
148178
if (Character.isLowerCase(c)) {
149-
// 累加钥匙数量
179+
// Count the number of keys
150180
++k;
151181
} else if (c == '@') {
152-
// 起点
182+
// Starting point
153183
si = i;
154184
sj = j;
155185
}
@@ -164,36 +194,36 @@ class Solution {
164194
for (int t = q.size(); t > 0; --t) {
165195
var p = q.poll();
166196
int i = p[0], j = p[1], state = p[2];
167-
// 找到所有钥匙,返回当前步数
197+
// If all keys are found, return the current step count
168198
if (state == (1 << k) - 1) {
169199
return ans;
170200
}
171-
// 往四个方向搜索
201+
// Search in the four directions
172202
for (int h = 0; h < 4; ++h) {
173203
int x = i + dirs[h], y = j + dirs[h + 1];
174-
// 在边界范围内
204+
// Within boundary limits
175205
if (x >= 0 && x < m && y >= 0 && y < n) {
176206
char c = grid[x].charAt(y);
177-
// 是墙,或者是锁,但此时没有对应的钥匙,无法通过
207+
// It's a wall, or it's a lock without the corresponding key
178208
if (c == '#'
179209
|| (Character.isUpperCase(c) && ((state >> (c - 'A')) & 1) == 0)) {
180210
continue;
181211
}
182212
int nxt = state;
183-
// 是钥匙
213+
// If it's a key
184214
if (Character.isLowerCase(c)) {
185-
// 更新状态
215+
// Update the state
186216
nxt |= 1 << (c - 'a');
187217
}
188-
// 此状态未访问过,入队
218+
// If this state has not been visited, enqueue it
189219
if (!vis[x][y][nxt]) {
190220
vis[x][y][nxt] = true;
191221
q.offer(new int[] {x, y, nxt});
192222
}
193223
}
194224
}
195225
}
196-
// 步数加一
226+
// Increment the step count
197227
++ans;
198228
}
199229
return -1;
@@ -215,9 +245,9 @@ public:
215245
for (int i = 0; i < m; ++i) {
216246
for (int j = 0; j < n; ++j) {
217247
char c = grid[i][j];
218-
// 累加钥匙数量
248+
// Count the number of keys
219249
if (islower(c)) ++k;
220-
// 起点
250+
// Starting point
221251
else if (c == '@')
222252
si = i, sj = j;
223253
}
@@ -230,28 +260,28 @@ public:
230260
for (int t = q.size(); t; --t) {
231261
auto [i, j, state] = q.front();
232262
q.pop();
233-
// 找到所有钥匙,返回当前步数
263+
// If all keys are found, return the current step count
234264
if (state == (1 << k) - 1) return ans;
235-
// 往四个方向搜索
265+
// Search in the four directions
236266
for (int h = 0; h < 4; ++h) {
237267
int x = i + dirs[h], y = j + dirs[h + 1];
238-
// 在边界范围内
268+
// Within boundary limits
239269
if (x >= 0 && x < m && y >= 0 && y < n) {
240270
char c = grid[x][y];
241-
// 是墙,或者是锁,但此时没有对应的钥匙,无法通过
271+
// It's a wall, or it's a lock without the corresponding key
242272
if (c == '#' || (isupper(c) && (state >> (c - 'A') & 1) == 0)) continue;
243273
int nxt = state;
244-
// 是钥匙,更新状态
274+
// If it's a key, update the state
245275
if (islower(c)) nxt |= 1 << (c - 'a');
246-
// 此状态未访问过,入队
276+
// If this state has not been visited, enqueue it
247277
if (!vis[x][y][nxt]) {
248278
vis[x][y][nxt] = true;
249279
q.push({x, y, nxt});
250280
}
251281
}
252282
}
253283
}
254-
// 步数加一
284+
// Increment the step count
255285
++ans;
256286
}
257287
return -1;
@@ -268,10 +298,10 @@ func shortestPathAllKeys(grid []string) int {
268298
for i, row := range grid {
269299
for j, c := range row {
270300
if c >= 'a' && c <= 'z' {
271-
// 累加钥匙数量
301+
// Count the number of keys
272302
k++
273303
} else if c == '@' {
274-
// 起点
304+
// Starting point
275305
si, sj = i, j
276306
}
277307
}
@@ -286,34 +316,34 @@ func shortestPathAllKeys(grid []string) int {
286316
p := q[0]
287317
q = q[1:]
288318
i, j, state := p.i, p.j, p.state
289-
// 找到所有钥匙,返回当前步数
319+
// If all keys are found, return the current step count
290320
if state == 1<<k-1 {
291321
return ans
292322
}
293-
// 往四个方向搜索
323+
// Search in the four directions
294324
for h := 0; h < 4; h++ {
295325
x, y := i+dirs[h], j+dirs[h+1]
296-
// 在边界范围内
326+
// Within boundary limits
297327
if x >= 0 && x < m && y >= 0 && y < n {
298328
c := grid[x][y]
299-
// 是墙,或者是锁,但此时没有对应的钥匙,无法通过
329+
// It's a wall, or it's a lock without the corresponding key
300330
if c == '#' || (c >= 'A' && c <= 'Z' && (state>>(c-'A')&1 == 0)) {
301331
continue
302332
}
303333
nxt := state
304-
// 是钥匙,更新状态
334+
// If it's a key, update the state
305335
if c >= 'a' && c <= 'z' {
306336
nxt |= 1 << (c - 'a')
307337
}
308-
// 此状态未访问过,入队
338+
// If this state has not been visited, enqueue it
309339
if !vis[tuple{x, y, nxt}] {
310340
vis[tuple{x, y, nxt}] = true
311341
q = append(q, tuple{x, y, nxt})
312342
}
313343
}
314344
}
315345
}
316-
// 步数加一
346+
// Increment the step count
317347
ans++
318348
}
319349
return -1

0 commit comments

Comments
 (0)