Skip to content

feat: add solutions to lc problem: No.0837 #4649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions solution/0800-0899/0837.New 21 Game/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,57 +75,57 @@ tags:
函数 $dfs(i)$ 的计算方法如下:

- 如果 $i \ge k$,那么停止抽取数字,如果 $i \le n$,返回 $1$,否则返回 $0$;
- 否则,可以在 $[1,..maxPts]$ 范围内抽取下一个数字 $j$,那么 $dfs(i) = \frac{1}{maxPts} \sum_{j=1}^{maxPts} dfs(i+j)$。
- 否则,可以在 $[1,..\textit{maxPts}]$ 范围内抽取下一个数字 $j$,那么 $dfs(i) = \frac{1}{maxPts} \sum_{j=1}^{maxPts} dfs(i+j)$。

这里我们可以使用记忆化搜索来加速计算。

以上方法的时间复杂度为 $O(k \times maxPts)$,会超出时间限制,我们需要优化一下。
以上方法的时间复杂度为 $O(k \times \textit{maxPts})$,会超出时间限制,我们需要优化一下。

当 $i \lt k$ 时,以下等式成立:

$$
\begin{aligned}
dfs(i) &= (dfs(i + 1) + dfs(i + 2) + \cdots + dfs(i + maxPts)) / maxPts & (1)
dfs(i) &= (dfs(i + 1) + dfs(i + 2) + \cdots + dfs(i + \textit{maxPts})) / \ & (1)
\end{aligned}
$$

当 $i \lt k - 1$ 时,以下等式成立:

$$
\begin{aligned}
dfs(i+1) &= (dfs(i + 2) + dfs(i + 3) + \cdots + dfs(i + maxPts + 1)) / maxPts & (2)
dfs(i+1) &= (dfs(i + 2) + dfs(i + 3) + \cdots + dfs(i + \textit{maxPts} + 1)) / \textit{maxPts} & (2)
\end{aligned}
$$

因此,当 $i \lt k-1$ 时,我们将等式 $(1)$ 减去等式 $(2)$,得到:

$$
\begin{aligned}
dfs(i) - dfs(i+1) &= (dfs(i + 1) - dfs(i + maxPts + 1)) / maxPts
dfs(i) - dfs(i+1) &= (dfs(i + 1) - dfs(i + \textit{maxPts} + 1)) / \textit{maxPts}
\end{aligned}
$$

即:

$$
\begin{aligned}
dfs(i) &= dfs(i + 1) + (dfs(i + 1) - dfs(i + maxPts + 1)) / maxPts
dfs(i) &= dfs(i + 1) + (dfs(i + 1) - dfs(i + \textit{maxPts} + 1)) / \textit{maxPts}
\end{aligned}
$$

如果 $i=k-1$,有:

$$
\begin{aligned}
dfs(i) &= dfs(k - 1) &= dfs(k) + dfs(k + 1) + \cdots + dfs(k + maxPts - 1) / maxPts & (3)
dfs(i) &= dfs(k - 1) &= dfs(k) + dfs(k + 1) + \cdots + dfs(k + \textit{maxPts} - 1) / \textit{maxPts} & (3)
\end{aligned}
$$

我们假设有 $i$ 个数不超过 $n$,那么 $k+i-1 \leq n$,又因为 $i\leq maxPts$,所以 $i \leq \min(n-k+1, maxPts)$,因此等式 $(3)$ 可以写成:
我们假设有 $i$ 个数不超过 $n$,那么 $k+i-1 \leq n$,又因为 $i\leq \textit{maxPts}$,所以 $i \leq \min(n-k+1, \textit{maxPts})$,因此等式 $(3)$ 可以写成:

$$
\begin{aligned}
dfs(k-1) &= \min(n-k+1, maxPts) / maxPts
dfs(k-1) &= \min(n-k+1, \textit{maxPts}) / \textit{maxPts}
\end{aligned}
$$

Expand All @@ -136,13 +136,13 @@ $$
dfs(i) &= \begin{cases}
1, & i \geq k, i \leq n \\
0, & i \geq k, i \gt n \\
\min(n-k+1, maxPts) / maxPts, & i = k - 1 \\
dfs(i + 1) + (dfs(i + 1) - dfs(i + maxPts + 1)) / maxPts, & i < k - 1
\min(n-k+1, \textit{maxPts}) / \textit{maxPts}, & i = k - 1 \\
dfs(i + 1) + (dfs(i + 1) - dfs(i + \textit{maxPts} + 1)) / \textit{maxPts}, & i < k - 1
\end{cases}
\end{aligned}
$$

时间复杂度 $O(k + maxPts)$,空间复杂度 $O(k + maxPts)$。其中 $k$ 为最大分数。
时间复杂度 $O(k + \textit{maxPts})$,空间复杂度 $O(k + \textit{maxPts})$。其中 $k$ 为最大分数。

<!-- tabs:start -->

Expand Down Expand Up @@ -246,7 +246,7 @@ func new21Game(n int, k int, maxPts int) float64 {

```ts
function new21Game(n: number, k: number, maxPts: number): number {
const f = new Array(k).fill(0);
const f: number[] = Array(k).fill(0);
const dfs = (i: number): number => {
if (i >= k) {
return i <= n ? 1 : 0;
Expand Down Expand Up @@ -275,13 +275,13 @@ function new21Game(n: number, k: number, maxPts: number): number {

定义 $f[i]$ 表示当前分数为 $i$ 时,到最终停止抽取数字时,分数不超过 $n$ 的概率。那么答案就是 $f[0]$。

当 $k \leq i \leq \min(n, k + maxPts - 1)$ 时,有 $f[i] = 1$。
当 $k \leq i \leq \min(n, k + \textit{maxPts} - 1)$ 时,有 $f[i] = 1$。

当 $i = k - 1$ 时,有 $f[i] = \min(n-k+1, maxPts) / maxPts$。
当 $i = k - 1$ 时,有 $f[i] = \min(n-k+1, \textit{maxPts}) / \textit{maxPts}$。

当 $i \lt k - 1$ 时,有 $f[i] = f[i + 1] + (f[i + 1] - f[i + maxPts + 1]) / maxPts$。
当 $i \lt k - 1$ 时,有 $f[i] = f[i + 1] + (f[i + 1] - f[i + \textit{maxPts} + 1]) / \textit{maxPts}$。

时间复杂度 $O(k + maxPts)$,空间复杂度 $O(k + maxPts)$。其中 $k$ 为最大分数。
时间复杂度 $O(k + \textit{maxPts})$,空间复杂度 $O(k + \textit{maxPts})$。其中 $k$ 为最大分数。

<!-- tabs:start -->

Expand Down Expand Up @@ -369,7 +369,7 @@ function new21Game(n: number, k: number, maxPts: number): number {
if (k === 0) {
return 1;
}
const f = new Array(k + maxPts).fill(0);
const f: number[] = Array(k + maxPts).fill(0);
for (let i = k; i < Math.min(n + 1, k + maxPts); ++i) {
f[i] = 1;
}
Expand Down
96 changes: 91 additions & 5 deletions solution/0800-0899/0837.New 21 Game/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,81 @@ In 6 out of 10 possibilities, she is at or below 6 points.

<!-- solution:start -->

### Solution 1
### Solution 1: Memoized Search

We design a function $dfs(i)$, which represents the probability that when the current score is $i$, the final score does not exceed $n$ when we stop drawing numbers. The answer is $dfs(0)$.

The calculation method of function $dfs(i)$ is as follows:

- If $i \ge k$, then we stop drawing numbers. If $i \le n$, return $1$, otherwise return $0$;
- Otherwise, we can draw the next number $j$ in the range $[1,..\textit{maxPts}]$, then $dfs(i) = \frac{1}{maxPts} \sum_{j=1}^{maxPts} dfs(i+j)$.

Here we can use memoized search to accelerate the calculation.

The time complexity of the above method is $O(k \times \textit{maxPts})$, which will exceed the time limit, so we need to optimize it.

When $i \lt k$, the following equation holds:

$$
\begin{aligned}
dfs(i) &= (dfs(i + 1) + dfs(i + 2) + \cdots + dfs(i + \textit{maxPts})) / \textit{maxPts} & (1)
\end{aligned}
$$

When $i \lt k - 1$, the following equation holds:

$$
\begin{aligned}
dfs(i+1) &= (dfs(i + 2) + dfs(i + 3) + \cdots + dfs(i + \textit{maxPts} + 1)) / \textit{maxPts} & (2)
\end{aligned}
$$

Therefore, when $i \lt k-1$, we subtract equation $(2)$ from equation $(1)$ to get:

$$
\begin{aligned}
dfs(i) - dfs(i+1) &= (dfs(i + 1) - dfs(i + \textit{maxPts} + 1)) / \textit{maxPts}
\end{aligned}
$$

That is:

$$
\begin{aligned}
dfs(i) &= dfs(i + 1) + (dfs(i + 1) - dfs(i + \textit{maxPts} + 1)) / \textit{maxPts}
\end{aligned}
$$

If $i=k-1$, we have:

$$
\begin{aligned}
dfs(i) &= dfs(k - 1) = (dfs(k) + dfs(k + 1) + \cdots + dfs(k + \textit{maxPts} - 1)) / \textit{maxPts} & (3)
\end{aligned}
$$

We assume there are $i$ numbers not exceeding $n$, then $k+i-1 \leq n$, and since $i\leq \textit{maxPts}$, we have $i \leq \min(n-k+1, \textit{maxPts})$, so equation $(3)$ can be written as:

$$
\begin{aligned}
dfs(k-1) &= \min(n-k+1, \textit{maxPts}) / \textit{maxPts}
\end{aligned}
$$

In summary, we have the following state transition equation:

$$
\begin{aligned}
dfs(i) &= \begin{cases}
1, & i \geq k, i \leq n \\
0, & i \geq k, i \gt n \\
\min(n-k+1, \textit{maxPts}) / \textit{maxPts}, & i = k - 1 \\
dfs(i + 1) + (dfs(i + 1) - dfs(i + \textit{maxPts} + 1)) / \textit{maxPts}, & i < k - 1
\end{cases}
\end{aligned}
$$

Time complexity $O(k + \textit{maxPts})$, space complexity $O(k + \textit{maxPts})$. Where $k$ is the maximum score.

<!-- tabs:start -->

Expand Down Expand Up @@ -125,7 +199,7 @@ class Solution {
public:
double new21Game(int n, int k, int maxPts) {
vector<double> f(k);
function<double(int)> dfs = [&](int i) -> double {
auto dfs = [&](this auto&& dfs, int i) -> double {
if (i >= k) {
return i <= n ? 1 : 0;
}
Expand Down Expand Up @@ -172,7 +246,7 @@ func new21Game(n int, k int, maxPts int) float64 {

```ts
function new21Game(n: number, k: number, maxPts: number): number {
const f = new Array(k).fill(0);
const f: number[] = Array(k).fill(0);
const dfs = (i: number): number => {
if (i >= k) {
return i <= n ? 1 : 0;
Expand All @@ -195,7 +269,19 @@ function new21Game(n: number, k: number, maxPts: number): number {

<!-- solution:start -->

### Solution 2
### Solution 2: Dynamic Programming

We can convert the memoized search in Solution 1 into dynamic programming.

Define $f[i]$ to represent the probability that when the current score is $i$, the final score does not exceed $n$ when we stop drawing numbers. The answer is $f[0]$.

When $k \leq i \leq \min(n, k + \textit{maxPts} - 1)$, we have $f[i] = 1$.

When $i = k - 1$, we have $f[i] = \min(n-k+1, \textit{maxPts}) / \textit{maxPts}$.

When $i \lt k - 1$, we have $f[i] = f[i + 1] + (f[i + 1] - f[i + \textit{maxPts} + 1]) / \textit{maxPts}$.

Time complexity $O(k + \textit{maxPts})$, space complexity $O(k + \textit{maxPts})$. Where $k$ is the maximum score.

<!-- tabs:start -->

Expand Down Expand Up @@ -283,7 +369,7 @@ function new21Game(n: number, k: number, maxPts: number): number {
if (k === 0) {
return 1;
}
const f = new Array(k + maxPts).fill(0);
const f: number[] = Array(k + maxPts).fill(0);
for (let i = k; i < Math.min(n + 1, k + maxPts); ++i) {
f[i] = 1;
}
Expand Down
4 changes: 2 additions & 2 deletions solution/0800-0899/0837.New 21 Game/Solution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class Solution {
public:
double new21Game(int n, int k, int maxPts) {
vector<double> f(k);
function<double(int)> dfs = [&](int i) -> double {
auto dfs = [&](this auto&& dfs, int i) -> double {
if (i >= k) {
return i <= n ? 1 : 0;
}
Expand All @@ -16,4 +16,4 @@ class Solution {
};
return dfs(0);
}
};
};
2 changes: 1 addition & 1 deletion solution/0800-0899/0837.New 21 Game/Solution.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function new21Game(n: number, k: number, maxPts: number): number {
const f = new Array(k).fill(0);
const f: number[] = Array(k).fill(0);
const dfs = (i: number): number => {
if (i >= k) {
return i <= n ? 1 : 0;
Expand Down
2 changes: 1 addition & 1 deletion solution/0800-0899/0837.New 21 Game/Solution2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ function new21Game(n: number, k: number, maxPts: number): number {
if (k === 0) {
return 1;
}
const f = new Array(k + maxPts).fill(0);
const f: number[] = Array(k + maxPts).fill(0);
for (let i = k; i < Math.min(n + 1, k + maxPts); ++i) {
f[i] = 1;
}
Expand Down