From e47bfbacf3601087c17f35dd1b590491425582a0 Mon Sep 17 00:00:00 2001
From: Libin YANG
Date: Sat, 29 Mar 2025 16:46:02 +0800
Subject: [PATCH 1/3] feat: add solutions to lc problems: No.1039,1041,1042
(#4305)
---
.../README.md | 19 ++++---
.../README_EN.md | 55 ++++++++++++++++++-
.../1041.Robot Bounded In Circle/README.md | 10 ++--
.../1041.Robot Bounded In Circle/README_EN.md | 19 ++++++-
.../README.md | 40 ++++++++++++--
.../README_EN.md | 48 ++++++++++++++--
.../Solution.rs | 27 +++++++++
.../Solution.ts | 6 +-
8 files changed, 197 insertions(+), 27 deletions(-)
create mode 100644 solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.rs
diff --git a/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README.md b/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README.md
index 9ed3d3a56d38a..be992f1578ac9 100644
--- a/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README.md
+++ b/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README.md
@@ -77,17 +77,17 @@ tags:
### 方法一:记忆化搜索
-我们设计一个函数 $dfs(i, j)$,表示将多边形的顶点 $i$ 到 $j$ 进行三角剖分后的最低分数。那么答案就是 $dfs(0, n - 1)$。
+我们设计一个函数 $\text{dfs}(i, j)$,表示将多边形的顶点 $i$ 到 $j$ 进行三角剖分后的最低分数。那么答案就是 $\text{dfs}(0, n - 1)$。
-函数 $dfs(i, j)$ 的计算过程如下:
+函数 $\text{dfs}(i, j)$ 的计算过程如下:
如果 $i + 1 = j$,说明多边形只有两个顶点,无法进行三角剖分,返回 $0$;
-否则,我们枚举 $i$ 和 $j$ 之间的一个顶点 $k$,即 $i \lt k \lt j$,将多边形的顶点 $i$ 到 $j$ 进行三角剖分,可以分为两个子问题:将多边形的顶点 $i$ 到 $k$ 进行三角剖分,以及将多边形的顶点 $k$ 到 $j$ 进行三角剖分。这两个子问题的最低分数分别为 $dfs(i, k)$ 和 $dfs(k, j)$,而顶点 $i$, $j$ 和 $k$ 构成的三角形的分数为 $values[i] \times values[k] \times values[j]$。那么,此次三角剖分的最低分数为 $dfs(i, k) + dfs(k, j) + values[i] \times values[k] \times values[j]$,我们取所有可能的最小值,即为 $dfs(i, j)$ 的值。
+否则,我们枚举 $i$ 和 $j$ 之间的一个顶点 $k$,即 $i \lt k \lt j$,将多边形的顶点 $i$ 到 $j$ 进行三角剖分,可以分为两个子问题:将多边形的顶点 $i$ 到 $k$ 进行三角剖分,以及将多边形的顶点 $k$ 到 $j$ 进行三角剖分。这两个子问题的最低分数分别为 $\text{dfs}(i, k)$ 和 $\text{dfs}(k, j)$,而顶点 $i$, $j$ 和 $k$ 构成的三角形的分数为 $\text{values}[i] \times \text{values}[k] \times \text{values}[j]$。那么,此次三角剖分的最低分数为 $\text{dfs}(i, k) + \text{dfs}(k, j) + \text{values}[i] \times \text{values}[k] \times \text{values}[j]$,我们取所有可能的最小值,即为 $\text{dfs}(i, j)$ 的值。
为了避免重复计算,我们可以使用记忆化搜索,即使用哈希表或者数组来存储已经计算过的函数值。
-最后,我们返回 $dfs(0, n - 1)$ 即可。
+最后,我们返回 $\text{dfs}(0, n - 1)$ 即可。
时间复杂度 $O(n^3)$,空间复杂度 $O(n^2)$。其中 $n$ 为多边形的顶点数。
@@ -230,7 +230,7 @@ function minScoreTriangulation(values: number[]): number {
对于 $f[i][j]$(这里要求 $i + 1 \lt j$),我们先将 $f[i][j]$ 初始化为 $\infty$。
-我们枚举 $i$ 和 $j$ 之间的一个顶点 $k$,即 $i \lt k \lt j$,将多边形的顶点 $i$ 到 $j$ 进行三角剖分,可以分为两个子问题:将多边形的顶点 $i$ 到 $k$ 进行三角剖分,以及将多边形的顶点 $k$ 到 $j$ 进行三角剖分。这两个子问题的最低分数分别为 $f[i][k]$ 和 $f[k][j]$,而顶点 $i$, $j$ 和 $k$ 构成的三角形的分数为 $values[i] \times values[k] \times values[j]$。那么,此次三角剖分的最低分数为 $f[i][k] + f[k][j] + values[i] \times values[k] \times values[j]$,我们取所有可能的最小值,即为 $f[i][j]$ 的值。
+我们枚举 $i$ 和 $j$ 之间的一个顶点 $k$,即 $i \lt k \lt j$,将多边形的顶点 $i$ 到 $j$ 进行三角剖分,可以分为两个子问题:将多边形的顶点 $i$ 到 $k$ 进行三角剖分,以及将多边形的顶点 $k$ 到 $j$ 进行三角剖分。这两个子问题的最低分数分别为 $f[i][k]$ 和 $f[k][j]$,而顶点 $i$, $j$ 和 $k$ 构成的三角形的分数为 $\text{values}[i] \times \text{values}[k] \times \text{values}[j]$。那么,此次三角剖分的最低分数为 $f[i][k] + f[k][j] + \text{values}[i] \times \text{values}[k] \times \text{values}[j]$,我们取所有可能的最小值,即为 $f[i][j]$ 的值。
综上,我们可以得到状态转移方程:
@@ -238,7 +238,8 @@ $$
f[i][j]=
\begin{cases}
0, & i+1=j \\
-\min_{i
-### 方法三
+### 方法三:动态规划(另一种实现方式)
+
+方法二中,我们提到了两种枚举方式。这里我们使用第二种方式,从小到大枚举区间长度 $l$,其中 $3 \leq l \leq n$,然后枚举区间左端点 $i$,那么可以得到右端点 $j=i + l - 1$。
+
+时间复杂度 $O(n^3)$,空间复杂度 $O(n^2)$。其中 $n$ 为多边形的顶点数。
diff --git a/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README_EN.md b/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README_EN.md
index dc5f2cece7f3d..cd7b0d350a1b9 100644
--- a/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README_EN.md
+++ b/solution/1000-1099/1039.Minimum Score Triangulation of Polygon/README_EN.md
@@ -80,7 +80,20 @@ The minimum score is 144.
-### Solution 1
+### Solution 1: Memoization
+
+We design a function $\text{dfs}(i, j)$, which represents the minimum score after triangulating the polygon from vertex $i$ to $j$. The answer is $\text{dfs}(0, n - 1)$.
+
+The calculation process of $\text{dfs}(i, j)$ is as follows:
+
+- If $i + 1 = j$, it means the polygon has only two vertices and cannot be triangulated, so we return $0$;
+- Otherwise, we enumerate a vertex $k$ between $i$ and $j$, i.e., $i \lt k \lt j$. Triangulating the polygon from vertex $i$ to $j$ can be divided into two subproblems: triangulating the polygon from vertex $i$ to $k$ and triangulating the polygon from vertex $k$ to $j$. The minimum scores of these two subproblems are $\text{dfs}(i, k)$ and $\text{dfs}(k, j)$, respectively. The score of the triangle formed by vertices $i$, $j$, and $k$ is $\text{values}[i] \times \text{values}[k] \times \text{values}[j]$. Thus, the minimum score for this triangulation is $\text{dfs}(i, k) + \text{dfs}(k, j) + \text{values}[i] \times \text{values}[k] \times \text{values}[j]$. We take the minimum value of all possibilities, which is the value of $\text{dfs}(i, j)$.
+
+To avoid repeated calculations, we can use memoization, i.e., use a hash table or an array to store the already computed function values.
+
+Finally, we return $\text{dfs}(0, n - 1)$.
+
+The time complexity is $O(n^3)$, and the space complexity is $O(n^2)$, where $n$ is the number of vertices in the polygon.
@@ -213,7 +226,39 @@ function minScoreTriangulation(values: number[]): number {
-### Solution 2
+### Solution 2: Dynamic Programming
+
+We can convert the memoization approach in Solution 1 into a dynamic programming approach.
+
+Define $f[i][j]$ as the minimum score after triangulating the polygon from vertex $i$ to $j$. Initially, $f[i][j] = 0$, and the answer is $f[0][n-1]$.
+
+For $f[i][j]$ (where $i + 1 \lt j$), we first initialize $f[i][j]$ to $\infty$.
+
+We enumerate a vertex $k$ between $i$ and $j$, i.e., $i \lt k \lt j$. Triangulating the polygon from vertex $i$ to $j$ can be divided into two subproblems: triangulating the polygon from vertex $i$ to $k$ and triangulating the polygon from vertex $k$ to $j$. The minimum scores of these two subproblems are $f[i][k]$ and $f[k][j]$, respectively. The score of the triangle formed by vertices $i$, $j$, and $k$ is $\text{values}[i] \times \text{values}[k] \times \text{values}[j]$. Thus, the minimum score for this triangulation is $f[i][k] + f[k][j] + \text{values}[i] \times \text{values}[k] \times \text{values}[j]$. We take the minimum value of all possibilities, which becomes the value of $f[i][j]$.
+
+In summary, we can derive the state transition equation:
+
+$$
+f[i][j]=
+\begin{cases}
+0, & i+1=j \\
+\infty, & i+1
@@ -318,7 +363,11 @@ function minScoreTriangulation(values: number[]): number {
-### Solution 3
+### Solution 3: Dynamic Programming (Alternative Implementation)
+
+In Solution 2, we mentioned two enumeration strategies. Here, we use the second strategy: enumerate the interval length $l$ from small to large, where $3 \leq l \leq n$. Then, enumerate the left endpoint $i$ of the interval, and the right endpoint can be calculated as $j = i + l - 1$.
+
+The time complexity is $O(n^3)$, and the space complexity is $O(n^2)$, where $n$ is the number of vertices in the polygon.
diff --git a/solution/1000-1099/1041.Robot Bounded In Circle/README.md b/solution/1000-1099/1041.Robot Bounded In Circle/README.md
index ebba2ee50f149..f455c915e819c 100644
--- a/solution/1000-1099/1041.Robot Bounded In Circle/README.md
+++ b/solution/1000-1099/1041.Robot Bounded In Circle/README.md
@@ -107,20 +107,20 @@ tags:
我们可以模拟机器人的行走过程,用一个变量 $k$ 表示机器人的方向,初始值为 $0$,表示机器人面向北方。变量 $k$ 的取值范围为 $[0, 3]$,分别表示机器人面向北、西、南、东。另外,我们用一个长度为 $4$ 的数组 $dist$ 记录机器人在四个方向上行走的距离,初始值为 $[0, 0, 0, 0]$。
-遍历指令字符串 `instructions`,如果当前指令为 `'L'`,那么机器人转向西方,即 $k = (k + 1) \bmod 4$;如果当前指令为 `'R'`,那么机器人转向东方,即 $k = (k + 3) \bmod 4$;否则,机器人在当前方向上行走一步,即 $dist[k]++$。
+遍历指令字符串 $\textit{instructions}$,如果当前指令为 `'L'`,那么机器人转向西方,即 $k = (k + 1) \bmod 4$;如果当前指令为 `'R'`,那么机器人转向东方,即 $k = (k + 3) \bmod 4$;否则,机器人在当前方向上行走一步,即 $dist[k]++$。
-如果给定的指令字符串 `instructions` 执行一遍后,使得机器人最终回到原点,即 $dist[0] = dist[2]$ 且 $dist[1] = dist[3]$,那么机器人一定会进入循环。因为无论重复多少次指令,机器人都回到了原点,所以机器人一定会进入循环。
+如果给定的指令字符串 $\textit{instructions}$ 执行一遍后,使得机器人最终回到原点,即 $dist[0] = dist[2]$ 且 $dist[1] = dist[3]$,那么机器人一定会进入循环。因为无论重复多少次指令,机器人都回到了原点,所以机器人一定会进入循环。
-如果给定的指令字符串 `instructions` 执行一遍后,机器人没回到原点,不妨假设此时机器人位于 $(x, y)$,且方向为 $k$。
+如果给定的指令字符串 $\textit{instructions}$ 执行一遍后,机器人没回到原点,不妨假设此时机器人位于 $(x, y)$,且方向为 $k$。
- 若 $k=0$,即机器人面向北方,那么执行第二遍指令后,坐标变化量是 $(x, y)$;继续执行第三遍指令后,坐标变化量还是 $(x, y)$...累加这些变化量,机器人最终会到 $(n \times x, n \times y)$,其中 $n$ 是一个正整数。由于机器人最终没有回到原点,即 $x \neq 0$ 或 $y \neq 0$,所以 $n \times x \neq 0$ 或 $n \times y \neq 0$,因此机器人不会进入循环;
- 若 $k=1$,即机器人面向西方,那么机器人执行第二遍指令后,坐标变化量是 $(-y, x)$;继续执行第三遍执行后,坐标变化量是 $(-x, -y)$;继续执行第四遍指令后,坐标变化量是 $(y, -x)$。累加这些坐标变化量,我们可以发现,机器人最终会回到原点 $(0, 0)$;
- 若 $k=2$,即机器人面向南方,那么执行第二遍指令后,坐标变化量是 $(-x, -y)$,累加这两次坐标变化量,我们可以发现,机器人最终会回到原点 $(0, 0)$;
- 若 $k=3$,即机器人面向东方,那么执行第二遍指令后,坐标变化量是 $(y, -x)$;继续执行第三遍指令后,坐标变化量是 $(-x, -y)$;继续执行第四遍指令后,坐标变化量是 $(-y, x)$。累加这些坐标变化量,我们可以发现,机器人最终会回到原点 $(0, 0)$。
-综上所述,如果给定的指令字符串 `instructions` 执行一遍后,机器人回到了原点,或者机器人的方向与初始方向不同,那么机器人一定会进入循环。
+综上所述,如果给定的指令字符串 $\textit{instructions}$ 执行一遍后,机器人回到了原点,或者机器人的方向与初始方向不同,那么机器人一定会进入循环。
-时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为指令字符串 `instructions` 的长度。
+时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为指令字符串 $\textit{instructions}$ 的长度。
diff --git a/solution/1000-1099/1041.Robot Bounded In Circle/README_EN.md b/solution/1000-1099/1041.Robot Bounded In Circle/README_EN.md
index a33cd28cfc5ff..228fc5525573d 100644
--- a/solution/1000-1099/1041.Robot Bounded In Circle/README_EN.md
+++ b/solution/1000-1099/1041.Robot Bounded In Circle/README_EN.md
@@ -102,7 +102,24 @@ Based on that, we return true.
-### Solution 1
+### Solution 1: Simulation
+
+We can simulate the robot's movement. Use a variable $k$ to represent the robot's direction, initialized to $0$, which means the robot is facing north. The variable $k$ can take values in the range $[0, 3]$, representing the robot facing north, west, south, and east, respectively. Additionally, we use an array $dist$ of length $4$ to record the distance the robot travels in the four directions, initialized to $[0, 0, 0, 0]$.
+
+Traverse the instruction string $\textit{instructions}$. If the current instruction is `'L'`, the robot turns west, i.e., $k = (k + 1) \bmod 4$; if the instruction is `'R'`, the robot turns east, i.e., $k = (k + 3) \bmod 4$; otherwise, the robot moves one step in the current direction, i.e., $dist[k]++$.
+
+If the given instruction string $\textit{instructions}$ is executed once and the robot returns to the origin, i.e., $dist[0] = dist[2]$ and $dist[1] = dist[3]$, then the robot will definitely enter a loop. This is because no matter how many times the instructions are repeated, the robot always returns to the origin, so it must enter a loop.
+
+If the given instruction string $\textit{instructions}$ is executed once and the robot does not return to the origin, suppose the robot is at $(x, y)$ and its direction is $k$.
+
+- If $k=0$, i.e., the robot is facing north, then after executing the instructions a second time, the coordinate change is $(x, y)$; after executing the instructions a third time, the coordinate change is still $(x, y)$... Accumulating these changes, the robot will eventually reach $(n \times x, n \times y)$, where $n$ is a positive integer. Since the robot does not return to the origin, i.e., $x \neq 0$ or $y \neq 0$, it follows that $n \times x \neq 0$ or $n \times y \neq 0$, so the robot will not enter a loop;
+- If $k=1$, i.e., the robot is facing west, then after executing the instructions a second time, the coordinate change is $(-y, x)$; after executing the instructions a third time, the coordinate change is $(-x, -y)$; after executing the instructions a fourth time, the coordinate change is $(y, -x)$. Accumulating these coordinate changes, we find that the robot will eventually return to the origin $(0, 0)$;
+- If $k=2$, i.e., the robot is facing south, then after executing the instructions a second time, the coordinate change is $(-x, -y)$. Accumulating these two coordinate changes, we find that the robot will eventually return to the origin $(0, 0)$;
+- If $k=3$, i.e., the robot is facing east, then after executing the instructions a second time, the coordinate change is $(y, -x)$; after executing the instructions a third time, the coordinate change is $(-x, -y)$; after executing the instructions a fourth time, the coordinate change is $(-y, x)$. Accumulating these coordinate changes, we find that the robot will eventually return to the origin $(0, 0)$.
+
+In conclusion, if the given instruction string $\textit{instructions}$ is executed once and the robot returns to the origin, or if the robot's direction is different from the initial direction, then the robot will definitely enter a loop.
+
+The time complexity is $O(n)$, and the space complexity is $O(1)$, where $n$ is the length of the instruction string $\textit{instructions}$.
diff --git a/solution/1000-1099/1042.Flower Planting With No Adjacent/README.md b/solution/1000-1099/1042.Flower Planting With No Adjacent/README.md
index ee54ba5be9491..bb6ffe8767ca4 100644
--- a/solution/1000-1099/1042.Flower Planting With No Adjacent/README.md
+++ b/solution/1000-1099/1042.Flower Planting With No Adjacent/README.md
@@ -77,7 +77,7 @@ tags:
### 方法一:枚举
-我们先根据数组 $paths$ 构建图 $g$,其中 $g[x]$ 表示与花园 $x$ 相邻的花园列表。
+我们先根据数组 $\textit{paths}$ 构建图 $g$,其中 $g[x]$ 表示与花园 $x$ 相邻的花园列表。
接下来,对于每个花园 $x$,我们先找出与 $x$ 相邻的花园 $y$,并将 $y$ 花园中种植的花的种类标记为已使用。然后我们从花的种类 $1$ 开始枚举,直到找到一个未被使用的花的种类 $c$,将 $c$ 标记为 $x$ 花园中种植的花的种类,然后继续枚举下一个花园。
@@ -200,14 +200,14 @@ func gardenNoAdj(n int, paths [][]int) []int {
```ts
function gardenNoAdj(n: number, paths: number[][]): number[] {
- const g: number[][] = new Array(n).fill(0).map(() => []);
+ const g: number[][] = Array.from({ length: n }, () => []);
for (const [x, y] of paths) {
g[x - 1].push(y - 1);
g[y - 1].push(x - 1);
}
- const ans: number[] = new Array(n).fill(0);
+ const ans: number[] = Array(n).fill(0);
for (let x = 0; x < n; ++x) {
- const used: boolean[] = new Array(5).fill(false);
+ const used: boolean[] = Array(5).fill(false);
for (const y of g[x]) {
used[ans[y]] = true;
}
@@ -222,6 +222,38 @@ function gardenNoAdj(n: number, paths: number[][]): number[] {
}
```
+#### Rust
+
+```rust
+impl Solution {
+ pub fn garden_no_adj(n: i32, paths: Vec>) -> Vec {
+ let n = n as usize;
+ let mut g = vec![vec![]; n];
+
+ for path in paths {
+ let (x, y) = (path[0] as usize - 1, path[1] as usize - 1);
+ g[x].push(y);
+ g[y].push(x);
+ }
+
+ let mut ans = vec![0; n];
+ for x in 0..n {
+ let mut used = [false; 5];
+ for &y in &g[x] {
+ used[ans[y] as usize] = true;
+ }
+ for c in 1..5 {
+ if !used[c] {
+ ans[x] = c as i32;
+ break;
+ }
+ }
+ }
+ ans
+ }
+}
+```
+
diff --git a/solution/1000-1099/1042.Flower Planting With No Adjacent/README_EN.md b/solution/1000-1099/1042.Flower Planting With No Adjacent/README_EN.md
index ea2ac2fd5070a..c299b2cab2a93 100644
--- a/solution/1000-1099/1042.Flower Planting With No Adjacent/README_EN.md
+++ b/solution/1000-1099/1042.Flower Planting With No Adjacent/README_EN.md
@@ -73,7 +73,15 @@ Hence, [1,2,3] is a valid answer. Other valid answers include [1,2,4], [1,4,2],
-### Solution 1
+### Solution 1: Enumeration
+
+We first construct a graph $g$ based on the array $\textit{paths}$, where $g[x]$ represents the list of gardens adjacent to garden $x$.
+
+Next, for each garden $x$, we first find the gardens $y$ adjacent to $x$ and mark the types of flowers planted in garden $y$ as used. Then, we enumerate the flower types starting from $1$ until we find a flower type $c$ that has not been used. We assign $c$ as the flower type for garden $x$ and continue to the next garden.
+
+After the enumeration is complete, we return the result.
+
+The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$, where $n$ is the number of gardens and $m$ is the number of paths.
@@ -190,14 +198,14 @@ func gardenNoAdj(n int, paths [][]int) []int {
```ts
function gardenNoAdj(n: number, paths: number[][]): number[] {
- const g: number[][] = new Array(n).fill(0).map(() => []);
+ const g: number[][] = Array.from({ length: n }, () => []);
for (const [x, y] of paths) {
g[x - 1].push(y - 1);
g[y - 1].push(x - 1);
}
- const ans: number[] = new Array(n).fill(0);
+ const ans: number[] = Array(n).fill(0);
for (let x = 0; x < n; ++x) {
- const used: boolean[] = new Array(5).fill(false);
+ const used: boolean[] = Array(5).fill(false);
for (const y of g[x]) {
used[ans[y]] = true;
}
@@ -212,6 +220,38 @@ function gardenNoAdj(n: number, paths: number[][]): number[] {
}
```
+#### Rust
+
+```rust
+impl Solution {
+ pub fn garden_no_adj(n: i32, paths: Vec>) -> Vec {
+ let n = n as usize;
+ let mut g = vec![vec![]; n];
+
+ for path in paths {
+ let (x, y) = (path[0] as usize - 1, path[1] as usize - 1);
+ g[x].push(y);
+ g[y].push(x);
+ }
+
+ let mut ans = vec![0; n];
+ for x in 0..n {
+ let mut used = [false; 5];
+ for &y in &g[x] {
+ used[ans[y] as usize] = true;
+ }
+ for c in 1..5 {
+ if !used[c] {
+ ans[x] = c as i32;
+ break;
+ }
+ }
+ }
+ ans
+ }
+}
+```
+
diff --git a/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.rs b/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.rs
new file mode 100644
index 0000000000000..8b459ac0191af
--- /dev/null
+++ b/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.rs
@@ -0,0 +1,27 @@
+impl Solution {
+ pub fn garden_no_adj(n: i32, paths: Vec>) -> Vec {
+ let n = n as usize;
+ let mut g = vec![vec![]; n];
+
+ for path in paths {
+ let (x, y) = (path[0] as usize - 1, path[1] as usize - 1);
+ g[x].push(y);
+ g[y].push(x);
+ }
+
+ let mut ans = vec![0; n];
+ for x in 0..n {
+ let mut used = [false; 5];
+ for &y in &g[x] {
+ used[ans[y] as usize] = true;
+ }
+ for c in 1..5 {
+ if !used[c] {
+ ans[x] = c as i32;
+ break;
+ }
+ }
+ }
+ ans
+ }
+}
diff --git a/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.ts b/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.ts
index 55699e2db0d13..10ab2958eb83c 100644
--- a/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.ts
+++ b/solution/1000-1099/1042.Flower Planting With No Adjacent/Solution.ts
@@ -1,12 +1,12 @@
function gardenNoAdj(n: number, paths: number[][]): number[] {
- const g: number[][] = new Array(n).fill(0).map(() => []);
+ const g: number[][] = Array.from({ length: n }, () => []);
for (const [x, y] of paths) {
g[x - 1].push(y - 1);
g[y - 1].push(x - 1);
}
- const ans: number[] = new Array(n).fill(0);
+ const ans: number[] = Array(n).fill(0);
for (let x = 0; x < n; ++x) {
- const used: boolean[] = new Array(5).fill(false);
+ const used: boolean[] = Array(5).fill(false);
for (const y of g[x]) {
used[ans[y]] = true;
}
From 6e9f695c95998898a36698a2dbfeb2c8ba2ac7d5 Mon Sep 17 00:00:00 2001
From: Libin YANG
Date: Sat, 29 Mar 2025 18:40:25 +0800
Subject: [PATCH 2/3] feat: add solutions to lc problem: No.1038 (#4306)
---
.../README.md | 70 +++++++-------
.../README_EN.md | 91 +++++++++++--------
.../Solution.cpp | 20 ++--
.../Solution.py | 6 +-
.../Solution.rs | 24 ++---
.../Solution.ts | 18 ++--
6 files changed, 126 insertions(+), 103 deletions(-)
diff --git a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README.md b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README.md
index 51d5dd2846f39..fa007bee123f2 100644
--- a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README.md
+++ b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README.md
@@ -87,12 +87,12 @@ tags:
# self.left = left
# self.right = right
class Solution:
- def bstToGst(self, root: TreeNode) -> TreeNode:
- def dfs(root):
- nonlocal s
+ def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
+ def dfs(root: Optional[TreeNode]):
if root is None:
return
dfs(root.right)
+ nonlocal s
s += root.val
root.val = s
dfs(root.left)
@@ -156,20 +156,20 @@ class Solution {
*/
class Solution {
public:
- int s = 0;
-
TreeNode* bstToGst(TreeNode* root) {
+ int s = 0;
+ auto dfs = [&](this auto&& dfs, TreeNode* root) {
+ if (!root) {
+ return;
+ }
+ dfs(root->right);
+ s += root->val;
+ root->val = s;
+ dfs(root->left);
+ };
dfs(root);
return root;
}
-
- void dfs(TreeNode* root) {
- if (!root) return;
- dfs(root->right);
- s += root->val;
- root->val = s;
- dfs(root->left);
- }
};
```
@@ -219,17 +219,17 @@ func bstToGst(root *TreeNode) *TreeNode {
*/
function bstToGst(root: TreeNode | null): TreeNode | null {
- const dfs = (root: TreeNode | null, sum: number) => {
- if (root == null) {
- return sum;
+ let s = 0;
+ const dfs = (root: TreeNode | null) => {
+ if (!root) {
+ return;
}
- const { val, left, right } = root;
- sum = dfs(right, sum) + val;
- root.val = sum;
- sum = dfs(left, sum);
- return sum;
+ dfs(root.right);
+ s += root.val;
+ root.val = s;
+ dfs(root.left);
};
- dfs(root, 0);
+ dfs(root);
return root;
}
```
@@ -255,22 +255,24 @@ function bstToGst(root: TreeNode | null): TreeNode | null {
// }
// }
// }
-use std::cell::RefCell;
use std::rc::Rc;
+use std::cell::RefCell;
+
impl Solution {
- fn dfs(root: &mut Option>>, mut sum: i32) -> i32 {
- if let Some(node) = root {
- let mut node = node.as_ref().borrow_mut();
- sum = Self::dfs(&mut node.right, sum) + node.val;
- node.val = sum;
- sum = Self::dfs(&mut node.left, sum);
- }
- sum
+ pub fn bst_to_gst(root: Option>>) -> Option>> {
+ let mut s = 0;
+ Self::dfs(&root, &mut s);
+ root
}
- pub fn bst_to_gst(mut root: Option>>) -> Option>> {
- Self::dfs(&mut root, 0);
- root
+ fn dfs(root: &Option>>, s: &mut i32) {
+ if let Some(node) = root {
+ let mut node = node.borrow_mut();
+ Self::dfs(&node.right, s);
+ *s += node.val;
+ node.val = *s;
+ Self::dfs(&node.left, s);
+ }
}
}
```
diff --git a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README_EN.md b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README_EN.md
index f0b0a6a082837..97d3ea002dab7 100644
--- a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README_EN.md
+++ b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/README_EN.md
@@ -64,7 +64,11 @@ tags:
-### Solution 1
+### Solution 1: Recursion
+
+Traverse the binary search tree in the order of "right-root-left". Accumulate all the node values encountered into $s$, and assign the accumulated value to the corresponding `node`.
+
+Time complexity is $O(n)$, and space complexity is $O(n)$, where $n$ is the number of nodes in the binary search tree.
@@ -78,12 +82,12 @@ tags:
# self.left = left
# self.right = right
class Solution:
- def bstToGst(self, root: TreeNode) -> TreeNode:
- def dfs(root):
- nonlocal s
+ def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
+ def dfs(root: Optional[TreeNode]):
if root is None:
return
dfs(root.right)
+ nonlocal s
s += root.val
root.val = s
dfs(root.left)
@@ -147,20 +151,20 @@ class Solution {
*/
class Solution {
public:
- int s = 0;
-
TreeNode* bstToGst(TreeNode* root) {
+ int s = 0;
+ auto dfs = [&](this auto&& dfs, TreeNode* root) {
+ if (!root) {
+ return;
+ }
+ dfs(root->right);
+ s += root->val;
+ root->val = s;
+ dfs(root->left);
+ };
dfs(root);
return root;
}
-
- void dfs(TreeNode* root) {
- if (!root) return;
- dfs(root->right);
- s += root->val;
- root->val = s;
- dfs(root->left);
- }
};
```
@@ -210,17 +214,17 @@ func bstToGst(root *TreeNode) *TreeNode {
*/
function bstToGst(root: TreeNode | null): TreeNode | null {
- const dfs = (root: TreeNode | null, sum: number) => {
- if (root == null) {
- return sum;
+ let s = 0;
+ const dfs = (root: TreeNode | null) => {
+ if (!root) {
+ return;
}
- const { val, left, right } = root;
- sum = dfs(right, sum) + val;
- root.val = sum;
- sum = dfs(left, sum);
- return sum;
+ dfs(root.right);
+ s += root.val;
+ root.val = s;
+ dfs(root.left);
};
- dfs(root, 0);
+ dfs(root);
return root;
}
```
@@ -246,22 +250,24 @@ function bstToGst(root: TreeNode | null): TreeNode | null {
// }
// }
// }
-use std::cell::RefCell;
use std::rc::Rc;
+use std::cell::RefCell;
+
impl Solution {
- fn dfs(root: &mut Option>>, mut sum: i32) -> i32 {
- if let Some(node) = root {
- let mut node = node.as_ref().borrow_mut();
- sum = Self::dfs(&mut node.right, sum) + node.val;
- node.val = sum;
- sum = Self::dfs(&mut node.left, sum);
- }
- sum
+ pub fn bst_to_gst(root: Option>>) -> Option>> {
+ let mut s = 0;
+ Self::dfs(&root, &mut s);
+ root
}
- pub fn bst_to_gst(mut root: Option>>) -> Option>> {
- Self::dfs(&mut root, 0);
- root
+ fn dfs(root: &Option>>, s: &mut i32) {
+ if let Some(node) = root {
+ let mut node = node.borrow_mut();
+ Self::dfs(&node.right, s);
+ *s += node.val;
+ node.val = *s;
+ Self::dfs(&node.left, s);
+ }
}
}
```
@@ -330,7 +336,20 @@ struct TreeNode* bstToGst(struct TreeNode* root) {
-### Solution 2
+### Solution 2: Morris Traversal
+
+Morris traversal does not require a stack, with a time complexity of $O(n)$ and a space complexity of $O(1)$. The core idea is as follows:
+
+Define $s$ as the cumulative sum of the node values in the binary search tree. Traverse the binary tree nodes:
+
+1. If the right subtree of the current node `root` is null, **add the current node value to $s$**, update the current node value to $s$, and move the current node to `root.left`.
+2. If the right subtree of the current node `root` is not null, find the leftmost node `next` in the right subtree (i.e., the successor node of `root` in an in-order traversal):
+ - If the left subtree of the successor node `next` is null, set the left subtree of `next` to point to the current node `root`, and move the current node to `root.right`.
+ - If the left subtree of the successor node `next` is not null, **add the current node value to $s$**, update the current node value to $s$, then set the left subtree of `next` to null (i.e., remove the link between `next` and `root`), and move the current node to `root.left`.
+3. Repeat the above steps until the binary tree nodes are null, at which point the traversal is complete.
+4. Finally, return the root node of the binary search tree.
+
+> Morris reverse in-order traversal follows the same idea as Morris in-order traversal, except that the traversal order changes from "left-root-right" to "right-root-left".
diff --git a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.cpp b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.cpp
index e3f8ad1c493cf..325fdd44c7aea 100644
--- a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.cpp
+++ b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.cpp
@@ -11,18 +11,18 @@
*/
class Solution {
public:
- int s = 0;
-
TreeNode* bstToGst(TreeNode* root) {
+ int s = 0;
+ auto dfs = [&](this auto&& dfs, TreeNode* root) {
+ if (!root) {
+ return;
+ }
+ dfs(root->right);
+ s += root->val;
+ root->val = s;
+ dfs(root->left);
+ };
dfs(root);
return root;
}
-
- void dfs(TreeNode* root) {
- if (!root) return;
- dfs(root->right);
- s += root->val;
- root->val = s;
- dfs(root->left);
- }
};
\ No newline at end of file
diff --git a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.py b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.py
index db01d320edb78..d03ef87bb5eee 100644
--- a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.py
+++ b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.py
@@ -5,12 +5,12 @@
# self.left = left
# self.right = right
class Solution:
- def bstToGst(self, root: TreeNode) -> TreeNode:
- def dfs(root):
- nonlocal s
+ def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
+ def dfs(root: Optional[TreeNode]):
if root is None:
return
dfs(root.right)
+ nonlocal s
s += root.val
root.val = s
dfs(root.left)
diff --git a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.rs b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.rs
index f2b6b87562fe8..55ab171abbfbc 100644
--- a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.rs
+++ b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.rs
@@ -18,19 +18,21 @@
// }
use std::cell::RefCell;
use std::rc::Rc;
+
impl Solution {
- fn dfs(root: &mut Option>>, mut sum: i32) -> i32 {
- if let Some(node) = root {
- let mut node = node.as_ref().borrow_mut();
- sum = Self::dfs(&mut node.right, sum) + node.val;
- node.val = sum;
- sum = Self::dfs(&mut node.left, sum);
- }
- sum
+ pub fn bst_to_gst(root: Option>>) -> Option>> {
+ let mut s = 0;
+ Self::dfs(&root, &mut s);
+ root
}
- pub fn bst_to_gst(mut root: Option>>) -> Option>> {
- Self::dfs(&mut root, 0);
- root
+ fn dfs(root: &Option>>, s: &mut i32) {
+ if let Some(node) = root {
+ let mut node = node.borrow_mut();
+ Self::dfs(&node.right, s);
+ *s += node.val;
+ node.val = *s;
+ Self::dfs(&node.left, s);
+ }
}
}
diff --git a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.ts b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.ts
index 2f90397898b6c..f2346fdc247d0 100644
--- a/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.ts
+++ b/solution/1000-1099/1038.Binary Search Tree to Greater Sum Tree/Solution.ts
@@ -13,16 +13,16 @@
*/
function bstToGst(root: TreeNode | null): TreeNode | null {
- const dfs = (root: TreeNode | null, sum: number) => {
- if (root == null) {
- return sum;
+ let s = 0;
+ const dfs = (root: TreeNode | null) => {
+ if (!root) {
+ return;
}
- const { val, left, right } = root;
- sum = dfs(right, sum) + val;
- root.val = sum;
- sum = dfs(left, sum);
- return sum;
+ dfs(root.right);
+ s += root.val;
+ root.val = s;
+ dfs(root.left);
};
- dfs(root, 0);
+ dfs(root);
return root;
}
From 9676f4ff35035cb376bc43ec2fe64e13943d1d45 Mon Sep 17 00:00:00 2001
From: yanglbme
Date: Sat, 29 Mar 2025 21:44:31 +0800
Subject: [PATCH 3/3] feat: add solutions to lc problem: No.1036
No.1036.Escape a Large Maze
---
.../1036.Escape a Large Maze/README.md | 316 +++++++++++-------
.../1036.Escape a Large Maze/README_EN.md | 316 +++++++++++-------
.../1036.Escape a Large Maze/Solution.cpp | 54 +--
.../1036.Escape a Large Maze/Solution.go | 51 +--
.../1036.Escape a Large Maze/Solution.java | 45 +--
.../1036.Escape a Large Maze/Solution.py | 27 +-
.../1036.Escape a Large Maze/Solution.rs | 90 +++--
.../1036.Escape a Large Maze/Solution.ts | 38 +++
.../1037.Valid Boomerang/README_EN.md | 13 +-
9 files changed, 613 insertions(+), 337 deletions(-)
create mode 100644 solution/1000-1099/1036.Escape a Large Maze/Solution.ts
diff --git a/solution/1000-1099/1036.Escape a Large Maze/README.md b/solution/1000-1099/1036.Escape a Large Maze/README.md
index b741ac6a3723c..c607db883ad99 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/README.md
+++ b/solution/1000-1099/1036.Escape a Large Maze/README.md
@@ -70,7 +70,13 @@ tags:
-### 方法一
+### 方法一:DFS
+
+题目相当于在一个 $10^6 \times 10^6$ 的网格中,给定源点和目标点,以及一小部分被封锁的点,问是否可以从源点到达目标点。
+
+由于被封锁的点数量很少,最终能封锁的区域大小不超过 $|blocked|^2 / 2$,因此,我们可以从源点和目标点出发,进行深度优先搜索,直到搜索到目标点或者搜索到的点数超过 $|blocked|^2 / 2$,如果都满足,则返回 $\textit{true}$。否则返回 $\textit{false}$。
+
+时间复杂度 $O(m)$,空间复杂度 $O(m)$,其中 $m$ 是被封锁的区域的大小,本题中 $m \leq |blocked|^2 / 2 = 200^2 / 2 = 20000$。
@@ -81,24 +87,21 @@ class Solution:
def isEscapePossible(
self, blocked: List[List[int]], source: List[int], target: List[int]
) -> bool:
- def dfs(source, target, seen):
- x, y = source
- if (
- not (0 <= x < 10**6 and 0 <= y < 10**6)
- or (x, y) in blocked
- or (x, y) in seen
- ):
- return False
- seen.add((x, y))
- if len(seen) > 20000 or source == target:
+ def dfs(source: List[int], target: List[int], vis: set) -> bool:
+ vis.add(tuple(source))
+ if len(vis) > m:
return True
- for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
- next = [x + a, y + b]
- if dfs(next, target, seen):
- return True
+ for a, b in pairwise(dirs):
+ x, y = source[0] + a, source[1] + b
+ if 0 <= x < n and 0 <= y < n and (x, y) not in s and (x, y) not in vis:
+ if [x, y] == target or dfs([x, y], target, vis):
+ return True
return False
- blocked = set((x, y) for x, y in blocked)
+ s = {(x, y) for x, y in blocked}
+ dirs = (-1, 0, 1, 0, -1)
+ n = 10**6
+ m = len(blocked) ** 2 // 2
return dfs(source, target, set()) and dfs(target, source, set())
```
@@ -106,69 +109,86 @@ class Solution:
```java
class Solution {
- private int[][] dirs = new int[][] {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
- private static final int N = (int) 1e6;
- private Set blocked;
+ private final int n = (int) 1e6;
+ private int m;
+ private Set s = new HashSet<>();
+ private final int[] dirs = {-1, 0, 1, 0, -1};
public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
- this.blocked = new HashSet<>();
- for (int[] b : blocked) {
- this.blocked.add(b[0] * N + b[1]);
+ for (var b : blocked) {
+ s.add(f(b[0], b[1]));
}
- return dfs(source, target, new HashSet<>()) && dfs(target, source, new HashSet<>());
- }
-
- private boolean dfs(int[] source, int[] target, Set seen) {
+ m = blocked.length * blocked.length / 2;
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
- if (sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N
- || blocked.contains(sx * N + sy) || seen.contains(sx * N + sy)) {
- return false;
- }
- seen.add(sx * N + sy);
- if (seen.size() > 20000 || (sx == target[0] && sy == target[1])) {
+ return dfs(sx, sy, tx, ty, new HashSet<>()) && dfs(tx, ty, sx, sy, new HashSet<>());
+ }
+
+ private boolean dfs(int sx, int sy, int tx, int ty, Set vis) {
+ if (vis.size() > m) {
return true;
}
- for (int[] dir : dirs) {
- if (dfs(new int[] {sx + dir[0], sy + dir[1]}, target, seen)) {
- return true;
+ for (int k = 0; k < 4; ++k) {
+ int x = sx + dirs[k], y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x == tx && y == ty) {
+ return true;
+ }
+ long key = f(x, y);
+ if (!s.contains(key) && vis.add(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
}
}
return false;
}
+
+ private long f(int i, int j) {
+ return (long) i * n + j;
+ }
}
```
#### C++
```cpp
-typedef unsigned long long ULL;
-
class Solution {
public:
- vector> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
- unordered_set blocked;
- int N = 1e6;
-
bool isEscapePossible(vector>& blocked, vector& source, vector& target) {
- this->blocked.clear();
- for (auto& b : blocked) this->blocked.insert((ULL) b[0] * N + b[1]);
- unordered_set s1;
- unordered_set s2;
- return dfs(source, target, s1) && dfs(target, source, s2);
- }
-
- bool dfs(vector& source, vector& target, unordered_set& seen) {
+ const int n = 1e6;
+ int m = blocked.size() * blocked.size() / 2;
+ using ll = long long;
+ unordered_set s;
+ const int dirs[5] = {-1, 0, 1, 0, -1};
+ auto f = [&](int i, int j) {
+ return (ll) i * n + j;
+ };
+ for (const auto& b : blocked) {
+ s.insert(f(b[0], b[1]));
+ }
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
- if (sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N || blocked.count((ULL) sx * N + sy) || seen.count((ULL) sx * N + sy)) return 0;
- seen.insert((ULL) sx * N + sy);
- if (seen.size() > 20000 || (sx == target[0] && sy == target[1])) return 1;
- for (auto& dir : dirs) {
- vector next = {sx + dir[0], sy + dir[1]};
- if (dfs(next, target, seen)) return 1;
- }
- return 0;
+ unordered_set vis1, vis2;
+ auto dfs = [&](this auto&& dfs, int sx, int sy, int tx, int ty, unordered_set& vis) -> bool {
+ vis.insert(f(sx, sy));
+ if (vis.size() > m) {
+ return true;
+ }
+ for (int k = 0; k < 4; ++k) {
+ int x = sx + dirs[k], y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x == tx && y == ty) {
+ return true;
+ }
+ auto key = f(x, y);
+ if (!s.contains(key) && !vis.contains(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ return dfs(sx, sy, tx, ty, vis1) && dfs(tx, ty, sx, sy, vis2);
}
};
```
@@ -177,84 +197,156 @@ public:
```go
func isEscapePossible(blocked [][]int, source []int, target []int) bool {
- const N = 1e6
- dirs := [4][2]int{{0, -1}, {0, 1}, {1, 0}, {-1, 0}}
- block := make(map[int]bool)
+ const n = 1_000_000
+ m := len(blocked) * len(blocked) / 2
+ dirs := [5]int{-1, 0, 1, 0, -1}
+
+ f := func(i, j int) int64 {
+ return int64(i*n + j)
+ }
+
+ s := make(map[int64]bool)
for _, b := range blocked {
- block[b[0]*N+b[1]] = true
+ s[f(b[0], b[1])] = true
}
- var dfs func(source, target []int, seen map[int]bool) bool
- dfs = func(source, target []int, seen map[int]bool) bool {
- sx, sy := source[0], source[1]
- tx, ty := target[0], target[1]
- if sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N || block[sx*N+sy] || seen[sx*N+sy] {
- return false
- }
- seen[sx*N+sy] = true
- if len(seen) > 20000 || (sx == target[0] && sy == target[1]) {
+
+ var dfs func(sx, sy, tx, ty int, vis map[int64]bool) bool
+ dfs = func(sx, sy, tx, ty int, vis map[int64]bool) bool {
+ key := f(sx, sy)
+ vis[key] = true
+ if len(vis) > m {
return true
}
- for _, dir := range dirs {
- next := []int{sx + dir[0], sy + dir[1]}
- if dfs(next, target, seen) {
- return true
+ for k := 0; k < 4; k++ {
+ x, y := sx+dirs[k], sy+dirs[k+1]
+ if x >= 0 && x < n && y >= 0 && y < n {
+ if x == tx && y == ty {
+ return true
+ }
+ key := f(x, y)
+ if !s[key] && !vis[key] && dfs(x, y, tx, ty, vis) {
+ return true
+ }
}
}
return false
}
- s1, s2 := make(map[int]bool), make(map[int]bool)
- return dfs(source, target, s1) && dfs(target, source, s2)
+
+ sx, sy := source[0], source[1]
+ tx, ty := target[0], target[1]
+ return dfs(sx, sy, tx, ty, map[int64]bool{}) && dfs(tx, ty, sx, sy, map[int64]bool{})
}
```
-#### Rust
+#### TypeScript
-```rust
-use std::collections::{HashSet, VecDeque};
+```ts
+function isEscapePossible(blocked: number[][], source: number[], target: number[]): boolean {
+ const n = 10 ** 6;
+ const m = (blocked.length ** 2) >> 1;
+ const dirs = [-1, 0, 1, 0, -1];
-const BOUNDARY: i32 = 1_000_000;
-const MAX: usize = 20000;
+ const s = new Set();
+ const f = (i: number, j: number): number => i * n + j;
-impl Solution {
- pub fn is_escape_possible(blocked: Vec>, source: Vec, target: Vec) -> bool {
- let mut block = HashSet::with_capacity(blocked.len());
- for b in blocked.iter() {
- block.insert((b[0], b[1]));
- }
- bfs(&block, &source, &target) && bfs(&block, &target, &source)
+ for (const [x, y] of blocked) {
+ s.add(f(x, y));
}
+
+ const dfs = (sx: number, sy: number, tx: number, ty: number, vis: Set): boolean => {
+ vis.add(f(sx, sy));
+ if (vis.size > m) {
+ return true;
+ }
+ for (let k = 0; k < 4; k++) {
+ const x = sx + dirs[k],
+ y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x === tx && y === ty) {
+ return true;
+ }
+ const key = f(x, y);
+ if (!s.has(key) && !vis.has(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ return (
+ dfs(source[0], source[1], target[0], target[1], new Set()) &&
+ dfs(target[0], target[1], source[0], source[1], new Set())
+ );
}
+```
-fn bfs(block: &HashSet<(i32, i32)>, source: &Vec, target: &Vec) -> bool {
- let dir = vec![(-1, 0), (1, 0), (0, -1), (0, 1)];
+#### Rust
- let mut queue = VecDeque::new();
- let mut vis = HashSet::new();
- queue.push_back((source[0], source[1]));
- vis.insert((source[0], source[1]));
+```rust
+use std::collections::HashSet;
- while !queue.is_empty() && vis.len() < MAX {
- let (x, y) = queue.pop_front().unwrap();
- if x == target[0] && y == target[1] {
- return true;
+impl Solution {
+ pub fn is_escape_possible(blocked: Vec>, source: Vec, target: Vec) -> bool {
+ const N: i64 = 1_000_000;
+ let m = (blocked.len() * blocked.len()) as i64 / 2;
+
+ let f = |i: i64, j: i64| -> i64 { i * N + j };
+
+ let mut s: HashSet = HashSet::new();
+ for b in &blocked {
+ s.insert(f(b[0] as i64, b[1] as i64));
}
- for (dx, dy) in dir.iter() {
- let (nx, ny) = (x + dx, y + dy);
- if nx < 0
- || nx >= BOUNDARY
- || ny < 0
- || ny >= BOUNDARY
- || vis.contains(&(nx, ny))
- || block.contains(&(nx, ny))
- {
- continue;
+
+ fn dfs(
+ sx: i64,
+ sy: i64,
+ tx: i64,
+ ty: i64,
+ s: &HashSet,
+ m: i64,
+ vis: &mut HashSet,
+ ) -> bool {
+ static DIRS: [i64; 5] = [-1, 0, 1, 0, -1];
+ let key = sx * 1_000_000 + sy;
+ vis.insert(key);
+ if vis.len() as i64 > m {
+ return true;
}
- queue.push_back((nx, ny));
- vis.insert((nx, ny));
+ for k in 0..4 {
+ let x = sx + DIRS[k];
+ let y = sy + DIRS[k + 1];
+ let key = x * 1_000_000 + y;
+ if x >= 0 && x < 1_000_000 && y >= 0 && y < 1_000_000 {
+ if x == tx && y == ty {
+ return true;
+ }
+ if !s.contains(&key) && vis.insert(key) && dfs(x, y, tx, ty, s, m, vis) {
+ return true;
+ }
+ }
+ }
+ false
}
- }
- vis.len() >= MAX
+ dfs(
+ source[0] as i64,
+ source[1] as i64,
+ target[0] as i64,
+ target[1] as i64,
+ &s,
+ m,
+ &mut HashSet::new(),
+ ) && dfs(
+ target[0] as i64,
+ target[1] as i64,
+ source[0] as i64,
+ source[1] as i64,
+ &s,
+ m,
+ &mut HashSet::new(),
+ )
+ }
}
```
diff --git a/solution/1000-1099/1036.Escape a Large Maze/README_EN.md b/solution/1000-1099/1036.Escape a Large Maze/README_EN.md
index eaa4a95a4b6d0..5b1e38c701603 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/README_EN.md
+++ b/solution/1000-1099/1036.Escape a Large Maze/README_EN.md
@@ -67,7 +67,13 @@ We cannot move south or west because we cannot go outside of the grid.
-### Solution 1
+### Solution 1: DFS
+
+The problem can be interpreted as determining whether it is possible to move from a source point to a target point in a $10^6 \times 10^6$ grid, given a small number of blocked points.
+
+Since the number of blocked points is small, the maximum area that can be blocked is no more than $|blocked|^2 / 2$. Therefore, we can perform a depth-first search (DFS) starting from both the source and the target points. The search continues until either the target point is reached or the number of visited points exceeds $|blocked|^2 / 2$. If either condition is satisfied, return $\textit{true}$. Otherwise, return $\textit{false}$.
+
+Time complexity is $O(m)$, and space complexity is $O(m)$, where $m$ is the size of the blocked region. In this problem, $m \leq |blocked|^2 / 2 = 200^2 / 2 = 20000$.
@@ -78,24 +84,21 @@ class Solution:
def isEscapePossible(
self, blocked: List[List[int]], source: List[int], target: List[int]
) -> bool:
- def dfs(source, target, seen):
- x, y = source
- if (
- not (0 <= x < 10**6 and 0 <= y < 10**6)
- or (x, y) in blocked
- or (x, y) in seen
- ):
- return False
- seen.add((x, y))
- if len(seen) > 20000 or source == target:
+ def dfs(source: List[int], target: List[int], vis: set) -> bool:
+ vis.add(tuple(source))
+ if len(vis) > m:
return True
- for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
- next = [x + a, y + b]
- if dfs(next, target, seen):
- return True
+ for a, b in pairwise(dirs):
+ x, y = source[0] + a, source[1] + b
+ if 0 <= x < n and 0 <= y < n and (x, y) not in s and (x, y) not in vis:
+ if [x, y] == target or dfs([x, y], target, vis):
+ return True
return False
- blocked = set((x, y) for x, y in blocked)
+ s = {(x, y) for x, y in blocked}
+ dirs = (-1, 0, 1, 0, -1)
+ n = 10**6
+ m = len(blocked) ** 2 // 2
return dfs(source, target, set()) and dfs(target, source, set())
```
@@ -103,69 +106,86 @@ class Solution:
```java
class Solution {
- private int[][] dirs = new int[][] {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
- private static final int N = (int) 1e6;
- private Set blocked;
+ private final int n = (int) 1e6;
+ private int m;
+ private Set s = new HashSet<>();
+ private final int[] dirs = {-1, 0, 1, 0, -1};
public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
- this.blocked = new HashSet<>();
- for (int[] b : blocked) {
- this.blocked.add(b[0] * N + b[1]);
+ for (var b : blocked) {
+ s.add(f(b[0], b[1]));
}
- return dfs(source, target, new HashSet<>()) && dfs(target, source, new HashSet<>());
- }
-
- private boolean dfs(int[] source, int[] target, Set seen) {
+ m = blocked.length * blocked.length / 2;
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
- if (sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N
- || blocked.contains(sx * N + sy) || seen.contains(sx * N + sy)) {
- return false;
- }
- seen.add(sx * N + sy);
- if (seen.size() > 20000 || (sx == target[0] && sy == target[1])) {
+ return dfs(sx, sy, tx, ty, new HashSet<>()) && dfs(tx, ty, sx, sy, new HashSet<>());
+ }
+
+ private boolean dfs(int sx, int sy, int tx, int ty, Set vis) {
+ if (vis.size() > m) {
return true;
}
- for (int[] dir : dirs) {
- if (dfs(new int[] {sx + dir[0], sy + dir[1]}, target, seen)) {
- return true;
+ for (int k = 0; k < 4; ++k) {
+ int x = sx + dirs[k], y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x == tx && y == ty) {
+ return true;
+ }
+ long key = f(x, y);
+ if (!s.contains(key) && vis.add(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
}
}
return false;
}
+
+ private long f(int i, int j) {
+ return (long) i * n + j;
+ }
}
```
#### C++
```cpp
-typedef unsigned long long ULL;
-
class Solution {
public:
- vector> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
- unordered_set blocked;
- int N = 1e6;
-
bool isEscapePossible(vector>& blocked, vector& source, vector& target) {
- this->blocked.clear();
- for (auto& b : blocked) this->blocked.insert((ULL) b[0] * N + b[1]);
- unordered_set s1;
- unordered_set s2;
- return dfs(source, target, s1) && dfs(target, source, s2);
- }
-
- bool dfs(vector& source, vector& target, unordered_set& seen) {
+ const int n = 1e6;
+ int m = blocked.size() * blocked.size() / 2;
+ using ll = long long;
+ unordered_set s;
+ const int dirs[5] = {-1, 0, 1, 0, -1};
+ auto f = [&](int i, int j) {
+ return (ll) i * n + j;
+ };
+ for (const auto& b : blocked) {
+ s.insert(f(b[0], b[1]));
+ }
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
- if (sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N || blocked.count((ULL) sx * N + sy) || seen.count((ULL) sx * N + sy)) return 0;
- seen.insert((ULL) sx * N + sy);
- if (seen.size() > 20000 || (sx == target[0] && sy == target[1])) return 1;
- for (auto& dir : dirs) {
- vector next = {sx + dir[0], sy + dir[1]};
- if (dfs(next, target, seen)) return 1;
- }
- return 0;
+ unordered_set vis1, vis2;
+ auto dfs = [&](this auto&& dfs, int sx, int sy, int tx, int ty, unordered_set& vis) -> bool {
+ vis.insert(f(sx, sy));
+ if (vis.size() > m) {
+ return true;
+ }
+ for (int k = 0; k < 4; ++k) {
+ int x = sx + dirs[k], y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x == tx && y == ty) {
+ return true;
+ }
+ auto key = f(x, y);
+ if (!s.contains(key) && !vis.contains(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ return dfs(sx, sy, tx, ty, vis1) && dfs(tx, ty, sx, sy, vis2);
}
};
```
@@ -174,84 +194,156 @@ public:
```go
func isEscapePossible(blocked [][]int, source []int, target []int) bool {
- const N = 1e6
- dirs := [4][2]int{{0, -1}, {0, 1}, {1, 0}, {-1, 0}}
- block := make(map[int]bool)
+ const n = 1_000_000
+ m := len(blocked) * len(blocked) / 2
+ dirs := [5]int{-1, 0, 1, 0, -1}
+
+ f := func(i, j int) int64 {
+ return int64(i*n + j)
+ }
+
+ s := make(map[int64]bool)
for _, b := range blocked {
- block[b[0]*N+b[1]] = true
+ s[f(b[0], b[1])] = true
}
- var dfs func(source, target []int, seen map[int]bool) bool
- dfs = func(source, target []int, seen map[int]bool) bool {
- sx, sy := source[0], source[1]
- tx, ty := target[0], target[1]
- if sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N || block[sx*N+sy] || seen[sx*N+sy] {
- return false
- }
- seen[sx*N+sy] = true
- if len(seen) > 20000 || (sx == target[0] && sy == target[1]) {
+
+ var dfs func(sx, sy, tx, ty int, vis map[int64]bool) bool
+ dfs = func(sx, sy, tx, ty int, vis map[int64]bool) bool {
+ key := f(sx, sy)
+ vis[key] = true
+ if len(vis) > m {
return true
}
- for _, dir := range dirs {
- next := []int{sx + dir[0], sy + dir[1]}
- if dfs(next, target, seen) {
- return true
+ for k := 0; k < 4; k++ {
+ x, y := sx+dirs[k], sy+dirs[k+1]
+ if x >= 0 && x < n && y >= 0 && y < n {
+ if x == tx && y == ty {
+ return true
+ }
+ key := f(x, y)
+ if !s[key] && !vis[key] && dfs(x, y, tx, ty, vis) {
+ return true
+ }
}
}
return false
}
- s1, s2 := make(map[int]bool), make(map[int]bool)
- return dfs(source, target, s1) && dfs(target, source, s2)
+
+ sx, sy := source[0], source[1]
+ tx, ty := target[0], target[1]
+ return dfs(sx, sy, tx, ty, map[int64]bool{}) && dfs(tx, ty, sx, sy, map[int64]bool{})
}
```
-#### Rust
+#### TypeScript
-```rust
-use std::collections::{HashSet, VecDeque};
+```ts
+function isEscapePossible(blocked: number[][], source: number[], target: number[]): boolean {
+ const n = 10 ** 6;
+ const m = (blocked.length ** 2) >> 1;
+ const dirs = [-1, 0, 1, 0, -1];
-const BOUNDARY: i32 = 1_000_000;
-const MAX: usize = 20000;
+ const s = new Set();
+ const f = (i: number, j: number): number => i * n + j;
-impl Solution {
- pub fn is_escape_possible(blocked: Vec>, source: Vec, target: Vec) -> bool {
- let mut block = HashSet::with_capacity(blocked.len());
- for b in blocked.iter() {
- block.insert((b[0], b[1]));
- }
- bfs(&block, &source, &target) && bfs(&block, &target, &source)
+ for (const [x, y] of blocked) {
+ s.add(f(x, y));
}
+
+ const dfs = (sx: number, sy: number, tx: number, ty: number, vis: Set): boolean => {
+ vis.add(f(sx, sy));
+ if (vis.size > m) {
+ return true;
+ }
+ for (let k = 0; k < 4; k++) {
+ const x = sx + dirs[k],
+ y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x === tx && y === ty) {
+ return true;
+ }
+ const key = f(x, y);
+ if (!s.has(key) && !vis.has(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ return (
+ dfs(source[0], source[1], target[0], target[1], new Set()) &&
+ dfs(target[0], target[1], source[0], source[1], new Set())
+ );
}
+```
-fn bfs(block: &HashSet<(i32, i32)>, source: &Vec, target: &Vec) -> bool {
- let dir = vec![(-1, 0), (1, 0), (0, -1), (0, 1)];
+#### Rust
- let mut queue = VecDeque::new();
- let mut vis = HashSet::new();
- queue.push_back((source[0], source[1]));
- vis.insert((source[0], source[1]));
+```rust
+use std::collections::HashSet;
- while !queue.is_empty() && vis.len() < MAX {
- let (x, y) = queue.pop_front().unwrap();
- if x == target[0] && y == target[1] {
- return true;
+impl Solution {
+ pub fn is_escape_possible(blocked: Vec>, source: Vec, target: Vec) -> bool {
+ const N: i64 = 1_000_000;
+ let m = (blocked.len() * blocked.len()) as i64 / 2;
+
+ let f = |i: i64, j: i64| -> i64 { i * N + j };
+
+ let mut s: HashSet = HashSet::new();
+ for b in &blocked {
+ s.insert(f(b[0] as i64, b[1] as i64));
}
- for (dx, dy) in dir.iter() {
- let (nx, ny) = (x + dx, y + dy);
- if nx < 0
- || nx >= BOUNDARY
- || ny < 0
- || ny >= BOUNDARY
- || vis.contains(&(nx, ny))
- || block.contains(&(nx, ny))
- {
- continue;
+
+ fn dfs(
+ sx: i64,
+ sy: i64,
+ tx: i64,
+ ty: i64,
+ s: &HashSet,
+ m: i64,
+ vis: &mut HashSet,
+ ) -> bool {
+ static DIRS: [i64; 5] = [-1, 0, 1, 0, -1];
+ let key = sx * 1_000_000 + sy;
+ vis.insert(key);
+ if vis.len() as i64 > m {
+ return true;
}
- queue.push_back((nx, ny));
- vis.insert((nx, ny));
+ for k in 0..4 {
+ let x = sx + DIRS[k];
+ let y = sy + DIRS[k + 1];
+ let key = x * 1_000_000 + y;
+ if x >= 0 && x < 1_000_000 && y >= 0 && y < 1_000_000 {
+ if x == tx && y == ty {
+ return true;
+ }
+ if !s.contains(&key) && vis.insert(key) && dfs(x, y, tx, ty, s, m, vis) {
+ return true;
+ }
+ }
+ }
+ false
}
- }
- vis.len() >= MAX
+ dfs(
+ source[0] as i64,
+ source[1] as i64,
+ target[0] as i64,
+ target[1] as i64,
+ &s,
+ m,
+ &mut HashSet::new(),
+ ) && dfs(
+ target[0] as i64,
+ target[1] as i64,
+ source[0] as i64,
+ source[1] as i64,
+ &s,
+ m,
+ &mut HashSet::new(),
+ )
+ }
}
```
diff --git a/solution/1000-1099/1036.Escape a Large Maze/Solution.cpp b/solution/1000-1099/1036.Escape a Large Maze/Solution.cpp
index 06d3f36870959..68fb3902b0f11 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/Solution.cpp
+++ b/solution/1000-1099/1036.Escape a Large Maze/Solution.cpp
@@ -1,29 +1,39 @@
-typedef unsigned long long ULL;
-
class Solution {
public:
- vector> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
- unordered_set blocked;
- int N = 1e6;
-
bool isEscapePossible(vector>& blocked, vector& source, vector& target) {
- this->blocked.clear();
- for (auto& b : blocked) this->blocked.insert((ULL) b[0] * N + b[1]);
- unordered_set s1;
- unordered_set s2;
- return dfs(source, target, s1) && dfs(target, source, s2);
- }
-
- bool dfs(vector& source, vector& target, unordered_set& seen) {
+ const int n = 1e6;
+ int m = blocked.size() * blocked.size() / 2;
+ using ll = long long;
+ unordered_set s;
+ const int dirs[5] = {-1, 0, 1, 0, -1};
+ auto f = [&](int i, int j) {
+ return (ll) i * n + j;
+ };
+ for (const auto& b : blocked) {
+ s.insert(f(b[0], b[1]));
+ }
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
- if (sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N || blocked.count((ULL) sx * N + sy) || seen.count((ULL) sx * N + sy)) return 0;
- seen.insert((ULL) sx * N + sy);
- if (seen.size() > 20000 || (sx == target[0] && sy == target[1])) return 1;
- for (auto& dir : dirs) {
- vector next = {sx + dir[0], sy + dir[1]};
- if (dfs(next, target, seen)) return 1;
- }
- return 0;
+ unordered_set vis1, vis2;
+ auto dfs = [&](this auto&& dfs, int sx, int sy, int tx, int ty, unordered_set& vis) -> bool {
+ vis.insert(f(sx, sy));
+ if (vis.size() > m) {
+ return true;
+ }
+ for (int k = 0; k < 4; ++k) {
+ int x = sx + dirs[k], y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x == tx && y == ty) {
+ return true;
+ }
+ auto key = f(x, y);
+ if (!s.contains(key) && !vis.contains(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ return dfs(sx, sy, tx, ty, vis1) && dfs(tx, ty, sx, sy, vis2);
}
};
\ No newline at end of file
diff --git a/solution/1000-1099/1036.Escape a Large Maze/Solution.go b/solution/1000-1099/1036.Escape a Large Maze/Solution.go
index 8ca3810178393..12009d78c98b3 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/Solution.go
+++ b/solution/1000-1099/1036.Escape a Large Maze/Solution.go
@@ -1,29 +1,40 @@
func isEscapePossible(blocked [][]int, source []int, target []int) bool {
- const N = 1e6
- dirs := [4][2]int{{0, -1}, {0, 1}, {1, 0}, {-1, 0}}
- block := make(map[int]bool)
+ const n = 1_000_000
+ m := len(blocked) * len(blocked) / 2
+ dirs := [5]int{-1, 0, 1, 0, -1}
+
+ f := func(i, j int) int64 {
+ return int64(i*n + j)
+ }
+
+ s := make(map[int64]bool)
for _, b := range blocked {
- block[b[0]*N+b[1]] = true
+ s[f(b[0], b[1])] = true
}
- var dfs func(source, target []int, seen map[int]bool) bool
- dfs = func(source, target []int, seen map[int]bool) bool {
- sx, sy := source[0], source[1]
- tx, ty := target[0], target[1]
- if sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N || block[sx*N+sy] || seen[sx*N+sy] {
- return false
- }
- seen[sx*N+sy] = true
- if len(seen) > 20000 || (sx == target[0] && sy == target[1]) {
+
+ var dfs func(sx, sy, tx, ty int, vis map[int64]bool) bool
+ dfs = func(sx, sy, tx, ty int, vis map[int64]bool) bool {
+ key := f(sx, sy)
+ vis[key] = true
+ if len(vis) > m {
return true
}
- for _, dir := range dirs {
- next := []int{sx + dir[0], sy + dir[1]}
- if dfs(next, target, seen) {
- return true
+ for k := 0; k < 4; k++ {
+ x, y := sx+dirs[k], sy+dirs[k+1]
+ if x >= 0 && x < n && y >= 0 && y < n {
+ if x == tx && y == ty {
+ return true
+ }
+ key := f(x, y)
+ if !s[key] && !vis[key] && dfs(x, y, tx, ty, vis) {
+ return true
+ }
}
}
return false
}
- s1, s2 := make(map[int]bool), make(map[int]bool)
- return dfs(source, target, s1) && dfs(target, source, s2)
-}
\ No newline at end of file
+
+ sx, sy := source[0], source[1]
+ tx, ty := target[0], target[1]
+ return dfs(sx, sy, tx, ty, map[int64]bool{}) && dfs(tx, ty, sx, sy, map[int64]bool{})
+}
diff --git a/solution/1000-1099/1036.Escape a Large Maze/Solution.java b/solution/1000-1099/1036.Escape a Large Maze/Solution.java
index a5564a2f72044..13f7f46e1b551 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/Solution.java
+++ b/solution/1000-1099/1036.Escape a Large Maze/Solution.java
@@ -1,32 +1,39 @@
class Solution {
- private int[][] dirs = new int[][] {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
- private static final int N = (int) 1e6;
- private Set blocked;
+ private final int n = (int) 1e6;
+ private int m;
+ private Set s = new HashSet<>();
+ private final int[] dirs = {-1, 0, 1, 0, -1};
public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
- this.blocked = new HashSet<>();
- for (int[] b : blocked) {
- this.blocked.add(b[0] * N + b[1]);
+ for (var b : blocked) {
+ s.add(f(b[0], b[1]));
}
- return dfs(source, target, new HashSet<>()) && dfs(target, source, new HashSet<>());
- }
-
- private boolean dfs(int[] source, int[] target, Set seen) {
+ m = blocked.length * blocked.length / 2;
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
- if (sx < 0 || sx >= N || sy < 0 || sy >= N || tx < 0 || tx >= N || ty < 0 || ty >= N
- || blocked.contains(sx * N + sy) || seen.contains(sx * N + sy)) {
- return false;
- }
- seen.add(sx * N + sy);
- if (seen.size() > 20000 || (sx == target[0] && sy == target[1])) {
+ return dfs(sx, sy, tx, ty, new HashSet<>()) && dfs(tx, ty, sx, sy, new HashSet<>());
+ }
+
+ private boolean dfs(int sx, int sy, int tx, int ty, Set vis) {
+ if (vis.size() > m) {
return true;
}
- for (int[] dir : dirs) {
- if (dfs(new int[] {sx + dir[0], sy + dir[1]}, target, seen)) {
- return true;
+ for (int k = 0; k < 4; ++k) {
+ int x = sx + dirs[k], y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x == tx && y == ty) {
+ return true;
+ }
+ long key = f(x, y);
+ if (!s.contains(key) && vis.add(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
}
}
return false;
}
+
+ private long f(int i, int j) {
+ return (long) i * n + j;
+ }
}
\ No newline at end of file
diff --git a/solution/1000-1099/1036.Escape a Large Maze/Solution.py b/solution/1000-1099/1036.Escape a Large Maze/Solution.py
index 4d6f067cf67ee..14455b34ec195 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/Solution.py
+++ b/solution/1000-1099/1036.Escape a Large Maze/Solution.py
@@ -2,22 +2,19 @@ class Solution:
def isEscapePossible(
self, blocked: List[List[int]], source: List[int], target: List[int]
) -> bool:
- def dfs(source, target, seen):
- x, y = source
- if (
- not (0 <= x < 10**6 and 0 <= y < 10**6)
- or (x, y) in blocked
- or (x, y) in seen
- ):
- return False
- seen.add((x, y))
- if len(seen) > 20000 or source == target:
+ def dfs(source: List[int], target: List[int], vis: set) -> bool:
+ vis.add(tuple(source))
+ if len(vis) > m:
return True
- for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]:
- next = [x + a, y + b]
- if dfs(next, target, seen):
- return True
+ for a, b in pairwise(dirs):
+ x, y = source[0] + a, source[1] + b
+ if 0 <= x < n and 0 <= y < n and (x, y) not in s and (x, y) not in vis:
+ if [x, y] == target or dfs([x, y], target, vis):
+ return True
return False
- blocked = set((x, y) for x, y in blocked)
+ s = {(x, y) for x, y in blocked}
+ dirs = (-1, 0, 1, 0, -1)
+ n = 10**6
+ m = len(blocked) ** 2 // 2
return dfs(source, target, set()) and dfs(target, source, set())
diff --git a/solution/1000-1099/1036.Escape a Large Maze/Solution.rs b/solution/1000-1099/1036.Escape a Large Maze/Solution.rs
index 19e88060f539e..9544204153c81 100644
--- a/solution/1000-1099/1036.Escape a Large Maze/Solution.rs
+++ b/solution/1000-1099/1036.Escape a Large Maze/Solution.rs
@@ -1,46 +1,64 @@
-use std::collections::{HashSet, VecDeque};
-
-const BOUNDARY: i32 = 1_000_000;
-const MAX: usize = 20000;
+use std::collections::HashSet;
impl Solution {
pub fn is_escape_possible(blocked: Vec>, source: Vec, target: Vec) -> bool {
- let mut block = HashSet::with_capacity(blocked.len());
- for b in blocked.iter() {
- block.insert((b[0], b[1]));
- }
- bfs(&block, &source, &target) && bfs(&block, &target, &source)
- }
-}
-
-fn bfs(block: &HashSet<(i32, i32)>, source: &Vec, target: &Vec) -> bool {
- let dir = vec![(-1, 0), (1, 0), (0, -1), (0, 1)];
+ const N: i64 = 1_000_000;
+ let m = (blocked.len() * blocked.len()) as i64 / 2;
- let mut queue = VecDeque::new();
- let mut vis = HashSet::new();
- queue.push_back((source[0], source[1]));
- vis.insert((source[0], source[1]));
+ let f = |i: i64, j: i64| -> i64 { i * N + j };
- while !queue.is_empty() && vis.len() < MAX {
- let (x, y) = queue.pop_front().unwrap();
- if x == target[0] && y == target[1] {
- return true;
+ let mut s: HashSet = HashSet::new();
+ for b in &blocked {
+ s.insert(f(b[0] as i64, b[1] as i64));
}
- for (dx, dy) in dir.iter() {
- let (nx, ny) = (x + dx, y + dy);
- if nx < 0
- || nx >= BOUNDARY
- || ny < 0
- || ny >= BOUNDARY
- || vis.contains(&(nx, ny))
- || block.contains(&(nx, ny))
- {
- continue;
+
+ fn dfs(
+ sx: i64,
+ sy: i64,
+ tx: i64,
+ ty: i64,
+ s: &HashSet,
+ m: i64,
+ vis: &mut HashSet,
+ ) -> bool {
+ static DIRS: [i64; 5] = [-1, 0, 1, 0, -1];
+ let key = sx * 1_000_000 + sy;
+ vis.insert(key);
+ if vis.len() as i64 > m {
+ return true;
+ }
+ for k in 0..4 {
+ let x = sx + DIRS[k];
+ let y = sy + DIRS[k + 1];
+ let key = x * 1_000_000 + y;
+ if x >= 0 && x < 1_000_000 && y >= 0 && y < 1_000_000 {
+ if x == tx && y == ty {
+ return true;
+ }
+ if !s.contains(&key) && vis.insert(key) && dfs(x, y, tx, ty, s, m, vis) {
+ return true;
+ }
+ }
}
- queue.push_back((nx, ny));
- vis.insert((nx, ny));
+ false
}
- }
- vis.len() >= MAX
+ dfs(
+ source[0] as i64,
+ source[1] as i64,
+ target[0] as i64,
+ target[1] as i64,
+ &s,
+ m,
+ &mut HashSet::new(),
+ ) && dfs(
+ target[0] as i64,
+ target[1] as i64,
+ source[0] as i64,
+ source[1] as i64,
+ &s,
+ m,
+ &mut HashSet::new(),
+ )
+ }
}
diff --git a/solution/1000-1099/1036.Escape a Large Maze/Solution.ts b/solution/1000-1099/1036.Escape a Large Maze/Solution.ts
new file mode 100644
index 0000000000000..c86ebb5257bf7
--- /dev/null
+++ b/solution/1000-1099/1036.Escape a Large Maze/Solution.ts
@@ -0,0 +1,38 @@
+function isEscapePossible(blocked: number[][], source: number[], target: number[]): boolean {
+ const n = 10 ** 6;
+ const m = (blocked.length ** 2) >> 1;
+ const dirs = [-1, 0, 1, 0, -1];
+
+ const s = new Set();
+ const f = (i: number, j: number): number => i * n + j;
+
+ for (const [x, y] of blocked) {
+ s.add(f(x, y));
+ }
+
+ const dfs = (sx: number, sy: number, tx: number, ty: number, vis: Set): boolean => {
+ vis.add(f(sx, sy));
+ if (vis.size > m) {
+ return true;
+ }
+ for (let k = 0; k < 4; k++) {
+ const x = sx + dirs[k],
+ y = sy + dirs[k + 1];
+ if (x >= 0 && x < n && y >= 0 && y < n) {
+ if (x === tx && y === ty) {
+ return true;
+ }
+ const key = f(x, y);
+ if (!s.has(key) && !vis.has(key) && dfs(x, y, tx, ty, vis)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ return (
+ dfs(source[0], source[1], target[0], target[1], new Set()) &&
+ dfs(target[0], target[1], source[0], source[1], new Set())
+ );
+}
diff --git a/solution/1000-1099/1037.Valid Boomerang/README_EN.md b/solution/1000-1099/1037.Valid Boomerang/README_EN.md
index 37a8651157d69..5c94dd25195a6 100644
--- a/solution/1000-1099/1037.Valid Boomerang/README_EN.md
+++ b/solution/1000-1099/1037.Valid Boomerang/README_EN.md
@@ -47,7 +47,18 @@ tags:
-### Solution 1
+### Solution 1: Slope Comparison
+
+Let the three points be $(x_1, y_1)$, $(x_2, y_2)$, and $(x_3, y_3)$. The formula for calculating the slope between two points is $\frac{y_2 - y_1}{x_2 - x_1}$.
+
+To ensure that the three points are not collinear, the condition $\frac{y_2 - y_1}{x_2 - x_1} \neq \frac{y_3 - y_2}{x_3 - x_2}$ must be satisfied. By transforming the equation, we get $(y_2 - y_1) \cdot (x_3 - x_2) \neq (y_3 - y_2) \cdot (x_2 - x_1)$.
+
+Note:
+
+1. When the slope between two points does not exist, i.e., $x_1 = x_2$, the transformed equation still holds.
+2. If there are precision issues with division in slope comparison, it can be converted to multiplication.
+
+Time complexity is $O(1)$.