diff --git a/solution/0800-0899/0837.New 21 Game/README.md b/solution/0800-0899/0837.New 21 Game/README.md index 961cb03d6ed67..d4701fce00556 100644 --- a/solution/0800-0899/0837.New 21 Game/README.md +++ b/solution/0800-0899/0837.New 21 Game/README.md @@ -75,17 +75,17 @@ 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} $$ @@ -93,7 +93,7 @@ $$ $$ \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} $$ @@ -101,7 +101,7 @@ $$ $$ \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} $$ @@ -109,7 +109,7 @@ $$ $$ \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} $$ @@ -117,15 +117,15 @@ $$ $$ \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} $$ @@ -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$ 为最大分数。 @@ -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; @@ -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$ 为最大分数。 @@ -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; } diff --git a/solution/0800-0899/0837.New 21 Game/README_EN.md b/solution/0800-0899/0837.New 21 Game/README_EN.md index 1f9a436258897..9366d369a576a 100644 --- a/solution/0800-0899/0837.New 21 Game/README_EN.md +++ b/solution/0800-0899/0837.New 21 Game/README_EN.md @@ -68,7 +68,81 @@ In 6 out of 10 possibilities, she is at or below 6 points. -### 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. @@ -125,7 +199,7 @@ class Solution { public: double new21Game(int n, int k, int maxPts) { vector f(k); - function dfs = [&](int i) -> double { + auto dfs = [&](this auto&& dfs, int i) -> double { if (i >= k) { return i <= n ? 1 : 0; } @@ -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; @@ -195,7 +269,19 @@ function new21Game(n: number, k: number, maxPts: number): number { -### 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. @@ -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; } diff --git a/solution/0800-0899/0837.New 21 Game/Solution.cpp b/solution/0800-0899/0837.New 21 Game/Solution.cpp index ce4e84b057e77..9e74144f83199 100644 --- a/solution/0800-0899/0837.New 21 Game/Solution.cpp +++ b/solution/0800-0899/0837.New 21 Game/Solution.cpp @@ -2,7 +2,7 @@ class Solution { public: double new21Game(int n, int k, int maxPts) { vector f(k); - function dfs = [&](int i) -> double { + auto dfs = [&](this auto&& dfs, int i) -> double { if (i >= k) { return i <= n ? 1 : 0; } @@ -16,4 +16,4 @@ class Solution { }; return dfs(0); } -}; \ No newline at end of file +}; diff --git a/solution/0800-0899/0837.New 21 Game/Solution.ts b/solution/0800-0899/0837.New 21 Game/Solution.ts index f08ffebda244d..f0582c0fca444 100644 --- a/solution/0800-0899/0837.New 21 Game/Solution.ts +++ b/solution/0800-0899/0837.New 21 Game/Solution.ts @@ -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; diff --git a/solution/0800-0899/0837.New 21 Game/Solution2.ts b/solution/0800-0899/0837.New 21 Game/Solution2.ts index fcaff0f51befd..c5b88f767f20b 100644 --- a/solution/0800-0899/0837.New 21 Game/Solution2.ts +++ b/solution/0800-0899/0837.New 21 Game/Solution2.ts @@ -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; }