diff --git a/solution/2100-2199/2151.Maximum Good People Based on Statements/README.md b/solution/2100-2199/2151.Maximum Good People Based on Statements/README.md index 659b191fe6d76..5340b3481f703 100644 --- a/solution/2100-2199/2151.Maximum Good People Based on Statements/README.md +++ b/solution/2100-2199/2151.Maximum Good People Based on Statements/README.md @@ -86,7 +86,7 @@ tags: - 在认为 0 是坏人但说真话的情况下,这组玩家中没有一个好人。 - 说假话。在这种情况下,1 是好人。 - 在认为 0 是坏人且说假话的情况下,这组玩家中只有一个好人。 -在最佳情况下,至多有一个好人,所以返回 1 。 +在最佳情况下,至多有一个好人,所以返回 1 。 注意,能得到此结论的方法不止一种。 @@ -120,17 +120,17 @@ tags: ```python class Solution: def maximumGood(self, statements: List[List[int]]) -> int: - def check(mask): + def check(mask: int) -> int: cnt = 0 - for i, s in enumerate(statements): - if (mask >> i) & 1: - for j, v in enumerate(s): - if v < 2 and ((mask >> j) & 1) != v: + for i, row in enumerate(statements): + if mask >> i & 1: + for j, x in enumerate(row): + if x < 2 and (mask >> j & 1) != x: return 0 cnt += 1 return cnt - return max(check(mask) for mask in range(1, 1 << len(statements))) + return max(check(i) for i in range(1, 1 << len(statements))) ``` #### Java diff --git a/solution/2100-2199/2151.Maximum Good People Based on Statements/README_EN.md b/solution/2100-2199/2151.Maximum Good People Based on Statements/README_EN.md index c6fafdd848c24..99da6be57830d 100644 --- a/solution/2100-2199/2151.Maximum Good People Based on Statements/README_EN.md +++ b/solution/2100-2199/2151.Maximum Good People Based on Statements/README_EN.md @@ -116,17 +116,17 @@ Note that there is more than one way to arrive at this conclusion. ```python class Solution: def maximumGood(self, statements: List[List[int]]) -> int: - def check(mask): + def check(mask: int) -> int: cnt = 0 - for i, s in enumerate(statements): - if (mask >> i) & 1: - for j, v in enumerate(s): - if v < 2 and ((mask >> j) & 1) != v: + for i, row in enumerate(statements): + if mask >> i & 1: + for j, x in enumerate(row): + if x < 2 and (mask >> j & 1) != x: return 0 cnt += 1 return cnt - return max(check(mask) for mask in range(1, 1 << len(statements))) + return max(check(i) for i in range(1, 1 << len(statements))) ``` #### Java diff --git a/solution/2100-2199/2151.Maximum Good People Based on Statements/Solution.py b/solution/2100-2199/2151.Maximum Good People Based on Statements/Solution.py index c24df6f17b498..b6567e50c9bd5 100644 --- a/solution/2100-2199/2151.Maximum Good People Based on Statements/Solution.py +++ b/solution/2100-2199/2151.Maximum Good People Based on Statements/Solution.py @@ -1,13 +1,13 @@ class Solution: def maximumGood(self, statements: List[List[int]]) -> int: - def check(mask): + def check(mask: int) -> int: cnt = 0 - for i, s in enumerate(statements): - if (mask >> i) & 1: - for j, v in enumerate(s): - if v < 2 and ((mask >> j) & 1) != v: + for i, row in enumerate(statements): + if mask >> i & 1: + for j, x in enumerate(row): + if x < 2 and (mask >> j & 1) != x: return 0 cnt += 1 return cnt - return max(check(mask) for mask in range(1, 1 << len(statements))) + return max(check(i) for i in range(1, 1 << len(statements))) diff --git a/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README.md b/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README.md index 167074aa40056..b8198b0178a64 100644 --- a/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README.md +++ b/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README.md @@ -111,16 +111,16 @@ tags: ### 方法一:记忆化搜索 -我们定义一个函数 $dfs(i)$,表示从第 $i$ 个水果开始购买所有水果所需要的最少金币数。那么答案就是 $dfs(1)$。 +我们定义一个函数 $\textit{dfs}(i)$,表示从第 $i$ 个水果开始购买所有水果所需要的最少金币数。那么答案就是 $\textit{dfs}(1)$。 -函数 $dfs(i)$ 的执行逻辑如下: +函数 $\textit{dfs}(i)$ 的执行逻辑如下: -- 如果 $i \times 2 \geq n$,说明只要买第 $i - 1$ 个水果即可,剩余的水果都可以免费获得,所以返回 $prices[i - 1]$。 -- 否则,我们可以购买水果 $i$,然后在接下来的 $i + 1$ 到 $2i + 1$ 个水果中选择一个水果 $j$ 开始购买,那么 $dfs(i) = prices[i - 1] + \min_{i + 1 \le j \le 2i + 1} dfs(j)$。 +- 如果 $i \times 2 \geq n$,说明只要买第 $i - 1$ 个水果即可,剩余的水果都可以免费获得,所以返回 $\textit{prices}[i - 1]$。 +- 否则,我们可以购买水果 $i$,然后在接下来的 $i + 1$ 到 $2i + 1$ 个水果中选择一个水果 $j$ 开始购买,那么 $\textit{dfs}(i) = \textit{prices}[i - 1] + \min_{i + 1 \le j \le 2i + 1} \textit{dfs}(j)$。 为了避免重复计算,我们使用记忆化搜索的方法,将已经计算过的结果保存起来,下次遇到相同的情况时,直接返回结果即可。 -时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $prices$ 的长度。 +时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{prices}$ 的长度。 @@ -250,11 +250,11 @@ function minimumCoins(prices: number[]): number { 与方法一类似,我们定义 $f[i]$ 表示从第 $i$ 个水果开始购买所有水果所需要的最少金币数。那么答案就是 $f[1]$。 -状态转移方程为 $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + prices[i - 1]$。 +状态转移方程为 $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + \textit{prices}[i - 1]$。 -在实现上,我们从后往前计算,并且可以直接在数组 $prices$ 上进行状态转移,这样可以节省空间。 +时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{prices}$ 的长度。 -时间复杂度 $O(n^2)$,其中 $n$ 为数组 $prices$ 的长度。空间复杂度 $O(1)$。 +在代码实现上,我们可以直接使用 $\textit{prices}$ 数组来存储 $f$ 数组,那么空间复杂度可以优化到 $O(1)$。 @@ -334,9 +334,9 @@ function minimumCoins(prices: number[]): number { 我们观察方法二中的状态转移方程,可以发现,对于每个 $i$,我们需要求出 $f[i + 1], f[i + 2], \cdots, f[2i + 1]$ 的最小值,并且随着 $i$ 的减小,这些值的范围也在减小。这实际上是求一个单调收窄的滑动窗口的最小值,我们可以使用单调队列来优化。 -我们从后往前计算,维护一个单调递增的队列 $q$,队列中存储的是下标。如果 $q$ 的队首元素大于 $i \times 2 + 1$,说明 $i$ 之后的元素都不会被用到,所以我们将队首元素出队。如果 $i$ 不大于 $(n - 1) / 2$,那么我们可以将 $prices[q[0] - 1]$ 加到 $prices[i - 1]$ 上,然后将 $i$ 加入队尾。如果 $q$ 的队尾元素对应的水果价格大于等于 $prices[i - 1]$,那么我们将队尾元素出队,直到队尾元素对应的水果价格小于 $prices[i - 1]$ 或者队列为空,然后将 $i$ 加入队尾。 +我们从后往前计算,维护一个单调递增的队列 $q$,队列中存储的是下标。如果 $q$ 的队首元素大于 $i \times 2 + 1$,说明 $i$ 之后的元素都不会被用到,所以我们将队首元素出队。如果 $i$ 不大于 $(n - 1) / 2$,那么我们可以将 $\textit{prices}[q[0] - 1]$ 加到 $\textit{prices}[i - 1]$ 上,然后将 $i$ 加入队尾。如果 $q$ 的队尾元素对应的水果价格大于等于 $\textit{prices}[i - 1]$,那么我们将队尾元素出队,直到队尾元素对应的水果价格小于 $\textit{prices}[i - 1]$ 或者队列为空,然后将 $i$ 加入队尾。 -时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $prices$ 的长度。 +时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{prices}$ 的长度。 diff --git a/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README_EN.md b/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README_EN.md index c269d8314e373..0de4665842e66 100644 --- a/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README_EN.md +++ b/solution/2900-2999/2944.Minimum Number of Coins for Fruits/README_EN.md @@ -107,7 +107,18 @@ tags: -### Solution 1 +### Solution 1: Memoization Search + +We define a function $\textit{dfs}(i)$ to represent the minimum number of coins needed to buy all the fruits starting from the $i$-th fruit. The answer is $\textit{dfs}(1)$. + +The execution logic of the function $\textit{dfs}(i)$ is as follows: + +- If $i \times 2 \geq n$, it means that buying the $(i - 1)$-th fruit is sufficient, and the remaining fruits can be obtained for free, so return $\textit{prices}[i - 1]$. +- Otherwise, we can buy fruit $i$, and then choose a fruit $j$ to start buying from the next $i + 1$ to $2i + 1$ fruits. Thus, $\textit{dfs}(i) = \textit{prices}[i - 1] + \min_{i + 1 \le j \le 2i + 1} \textit{dfs}(j)$. + +To avoid redundant calculations, we use memoization to store the results that have already been computed. When encountering the same situation again, we directly return the result. + +The time complexity is $O(n^2)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{prices}$. @@ -231,7 +242,17 @@ function minimumCoins(prices: number[]): number { -### Solution 2 +### Solution 2: Dynamic Programming + +We can rewrite the memoization search in Solution 1 into a dynamic programming form. + +Similar to Solution 1, we define $f[i]$ to represent the minimum number of coins needed to buy all the fruits starting from the $i$-th fruit. The answer is $f[1]$. + +The state transition equation is $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + \textit{prices}[i - 1]$. + +The time complexity is $O(n^2)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{prices}$. + +In the code implementation, we can directly use the $\textit{prices}$ array to store the $f$ array, thus optimizing the space complexity to $O(1)$. @@ -307,7 +328,13 @@ function minimumCoins(prices: number[]): number { -### Solution 3 +### Solution 3: Dynamic Programming + Monotonic Queue Optimization + +Observing the state transition equation in Solution 2, we can see that for each $i$, we need to find the minimum value of $f[i + 1], f[i + 2], \cdots, f[2i + 1]$. As $i$ decreases, the range of these values also decreases. This is essentially finding the minimum value in a sliding window with a narrowing range, which can be optimized using a monotonic queue. + +We calculate from back to front, maintaining a monotonically increasing queue $q$, where the queue stores indices. If the front element of $q$ is greater than $i \times 2 + 1$, it means that the elements after $i$ will not be used, so we dequeue the front element. If $i$ is not greater than $(n - 1) / 2$, we can add $\textit{prices}[q[0] - 1]$ to $\textit{prices}[i - 1]$, and then add $i$ to the back of the queue. If the fruit price corresponding to the back element of $q$ is greater than or equal to $\textit{prices}[i - 1]$, we dequeue the back element until the fruit price corresponding to the back element is less than $\textit{prices}[i - 1]$ or the queue is empty, then add $i$ to the back of the queue. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{prices}$.