Skip to content

Commit 5c1ede2

Browse files
authored
feat: add solutions to lc problem: No.2151 (doocs#3987)
No.2151.Maximum Good People Based on Statements
1 parent f488c5c commit 5c1ede2

File tree

5 files changed

+59
-32
lines changed

5 files changed

+59
-32
lines changed

solution/2100-2199/2151.Maximum Good People Based on Statements/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ tags:
8686
- <strong>在认为 0 是坏人但说真话的情况下,这组玩家中没有一个好人。</strong>
8787
- 说假话。在这种情况下,1 是好人。
8888
- <strong>在认为 0 是坏人且说假话的情况下,这组玩家中只有一个好人。</strong>
89-
在最佳情况下,至多有一个好人,所以返回 1 。
89+
在最佳情况下,至多有一个好人,所以返回 1 。
9090
注意,能得到此结论的方法不止一种。
9191
</pre>
9292

@@ -120,17 +120,17 @@ tags:
120120
```python
121121
class Solution:
122122
def maximumGood(self, statements: List[List[int]]) -> int:
123-
def check(mask):
123+
def check(mask: int) -> int:
124124
cnt = 0
125-
for i, s in enumerate(statements):
126-
if (mask >> i) & 1:
127-
for j, v in enumerate(s):
128-
if v < 2 and ((mask >> j) & 1) != v:
125+
for i, row in enumerate(statements):
126+
if mask >> i & 1:
127+
for j, x in enumerate(row):
128+
if x < 2 and (mask >> j & 1) != x:
129129
return 0
130130
cnt += 1
131131
return cnt
132132

133-
return max(check(mask) for mask in range(1, 1 << len(statements)))
133+
return max(check(i) for i in range(1, 1 << len(statements)))
134134
```
135135

136136
#### Java

solution/2100-2199/2151.Maximum Good People Based on Statements/README_EN.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,17 @@ Note that there is more than one way to arrive at this conclusion.
116116
```python
117117
class Solution:
118118
def maximumGood(self, statements: List[List[int]]) -> int:
119-
def check(mask):
119+
def check(mask: int) -> int:
120120
cnt = 0
121-
for i, s in enumerate(statements):
122-
if (mask >> i) & 1:
123-
for j, v in enumerate(s):
124-
if v < 2 and ((mask >> j) & 1) != v:
121+
for i, row in enumerate(statements):
122+
if mask >> i & 1:
123+
for j, x in enumerate(row):
124+
if x < 2 and (mask >> j & 1) != x:
125125
return 0
126126
cnt += 1
127127
return cnt
128128

129-
return max(check(mask) for mask in range(1, 1 << len(statements)))
129+
return max(check(i) for i in range(1, 1 << len(statements)))
130130
```
131131

132132
#### Java
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
class Solution:
22
def maximumGood(self, statements: List[List[int]]) -> int:
3-
def check(mask):
3+
def check(mask: int) -> int:
44
cnt = 0
5-
for i, s in enumerate(statements):
6-
if (mask >> i) & 1:
7-
for j, v in enumerate(s):
8-
if v < 2 and ((mask >> j) & 1) != v:
5+
for i, row in enumerate(statements):
6+
if mask >> i & 1:
7+
for j, x in enumerate(row):
8+
if x < 2 and (mask >> j & 1) != x:
99
return 0
1010
cnt += 1
1111
return cnt
1212

13-
return max(check(mask) for mask in range(1, 1 << len(statements)))
13+
return max(check(i) for i in range(1, 1 << len(statements)))

solution/2900-2999/2944.Minimum Number of Coins for Fruits/README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,16 @@ tags:
111111

112112
### 方法一:记忆化搜索
113113

114-
我们定义一个函数 $dfs(i)$,表示从第 $i$ 个水果开始购买所有水果所需要的最少金币数。那么答案就是 $dfs(1)$。
114+
我们定义一个函数 $\textit{dfs}(i)$,表示从第 $i$ 个水果开始购买所有水果所需要的最少金币数。那么答案就是 $\textit{dfs}(1)$。
115115

116-
函数 $dfs(i)$ 的执行逻辑如下:
116+
函数 $\textit{dfs}(i)$ 的执行逻辑如下:
117117

118-
- 如果 $i \times 2 \geq n$,说明只要买第 $i - 1$ 个水果即可,剩余的水果都可以免费获得,所以返回 $prices[i - 1]$。
119-
- 否则,我们可以购买水果 $i$,然后在接下来的 $i + 1$ 到 $2i + 1$ 个水果中选择一个水果 $j$ 开始购买,那么 $dfs(i) = prices[i - 1] + \min_{i + 1 \le j \le 2i + 1} dfs(j)$。
118+
- 如果 $i \times 2 \geq n$,说明只要买第 $i - 1$ 个水果即可,剩余的水果都可以免费获得,所以返回 $\textit{prices}[i - 1]$。
119+
- 否则,我们可以购买水果 $i$,然后在接下来的 $i + 1$ 到 $2i + 1$ 个水果中选择一个水果 $j$ 开始购买,那么 $\textit{dfs}(i) = \textit{prices}[i - 1] + \min_{i + 1 \le j \le 2i + 1} \textit{dfs}(j)$。
120120

121121
为了避免重复计算,我们使用记忆化搜索的方法,将已经计算过的结果保存起来,下次遇到相同的情况时,直接返回结果即可。
122122

123-
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $prices$ 的长度。
123+
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{prices}$ 的长度。
124124

125125
<!-- tabs:start -->
126126

@@ -250,11 +250,11 @@ function minimumCoins(prices: number[]): number {
250250

251251
与方法一类似,我们定义 $f[i]$ 表示从第 $i$ 个水果开始购买所有水果所需要的最少金币数。那么答案就是 $f[1]$。
252252

253-
状态转移方程为 $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + prices[i - 1]$。
253+
状态转移方程为 $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + \textit{prices}[i - 1]$。
254254

255-
在实现上,我们从后往前计算,并且可以直接在数组 $prices$ 上进行状态转移,这样可以节省空间
255+
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{prices}$ 的长度
256256

257-
时间复杂度 $O(n^2)$,其中 $n$ 为数组 $prices$ 的长度。空间复杂度 $O(1)$。
257+
在代码实现上,我们可以直接使用 $\textit{prices}$ 数组来存储 $f$ 数组,那么空间复杂度可以优化到 $O(1)$。
258258

259259
<!-- tabs:start -->
260260

@@ -334,9 +334,9 @@ function minimumCoins(prices: number[]): number {
334334

335335
我们观察方法二中的状态转移方程,可以发现,对于每个 $i$,我们需要求出 $f[i + 1], f[i + 2], \cdots, f[2i + 1]$ 的最小值,并且随着 $i$ 的减小,这些值的范围也在减小。这实际上是求一个单调收窄的滑动窗口的最小值,我们可以使用单调队列来优化。
336336

337-
我们从后往前计算,维护一个单调递增的队列 $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$ 加入队尾。
337+
我们从后往前计算,维护一个单调递增的队列 $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$ 加入队尾。
338338

339-
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $prices$ 的长度。
339+
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{prices}$ 的长度。
340340

341341
<!-- tabs:start -->
342342

solution/2900-2999/2944.Minimum Number of Coins for Fruits/README_EN.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,18 @@ tags:
107107

108108
<!-- solution:start -->
109109

110-
### Solution 1
110+
### Solution 1: Memoization Search
111+
112+
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)$.
113+
114+
The execution logic of the function $\textit{dfs}(i)$ is as follows:
115+
116+
- 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]$.
117+
- 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)$.
118+
119+
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.
120+
121+
The time complexity is $O(n^2)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{prices}$.
111122

112123
<!-- tabs:start -->
113124

@@ -231,7 +242,17 @@ function minimumCoins(prices: number[]): number {
231242

232243
<!-- solution:start -->
233244

234-
### Solution 2
245+
### Solution 2: Dynamic Programming
246+
247+
We can rewrite the memoization search in Solution 1 into a dynamic programming form.
248+
249+
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]$.
250+
251+
The state transition equation is $f[i] = \min_{i + 1 \le j \le 2i + 1} f[j] + \textit{prices}[i - 1]$.
252+
253+
The time complexity is $O(n^2)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{prices}$.
254+
255+
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)$.
235256

236257
<!-- tabs:start -->
237258

@@ -307,7 +328,13 @@ function minimumCoins(prices: number[]): number {
307328

308329
<!-- solution:start -->
309330

310-
### Solution 3
331+
### Solution 3: Dynamic Programming + Monotonic Queue Optimization
332+
333+
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.
334+
335+
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.
336+
337+
The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{prices}$.
311338

312339
<!-- tabs:start -->
313340

0 commit comments

Comments
 (0)