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; }