Skip to content

Commit 4de0fe7

Browse files
authored
feat: add solutions to lcof problem: No.60 (#2911)
面试题 60. n 个骰子的点数
1 parent 5284f9f commit 4de0fe7

File tree

10 files changed

+284
-55
lines changed

10 files changed

+284
-55
lines changed

lcof/面试题60. n个骰子的点数/README.md

+140-29
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ $$
5151
f[i][j] = \sum_{k=1}^6 f[i-1][j-k]
5252
$$
5353

54-
其中 $k$ 表示当前骰子的点数,$f[i-1][j-k]$ 表示投掷 $i-1$ 个骰子,点数和为 $j-k$ 的方案数。
54+
其中 $k$ 表示当前骰子的点数,$f[i-1][j-k]$ 表示投掷 $i-1$ 个骰子,点数和为 $j-k$ 的方案数。
5555

56-
最终我们需要求的是 $f[n][n \sim 6n]$ 的和,即投掷 $n$ 个骰子,点数和为 $n \sim 6n$ 的方案数之和
56+
初始条件为 $f[1][j] = 1$,表示投掷一个骰子,点数和为 $j$ 的方案数为 $1$
5757

58-
注意到 $f[i][j]$ 的值只与 $f[i-1][j-k]$ 有关,因此我们可以使用滚动数组的方法将空间复杂度降低到 $O(6n)$。
58+
最终,我们要求的答案即为 $\frac{f[n][j]}{6^n}$,其中 $n$ 为骰子个数,而 $j$ 的取值范围为 $[n, 6n]$。
5959

6060
时间复杂度 $O(n^2)$,空间复杂度 $O(6n)$。其中 $n$ 为骰子个数。
6161

@@ -75,7 +75,7 @@ class Solution:
7575
if j - k >= 0:
7676
f[i][j] += f[i - 1][j - k]
7777
m = pow(6, n)
78-
return [f[n][i] / m for i in range(n, 6 * n + 1)]
78+
return [f[n][j] / m for j in range(n, 6 * n + 1)]
7979
```
8080

8181
#### Java
@@ -98,8 +98,8 @@ class Solution {
9898
}
9999
double m = Math.pow(6, n);
100100
double[] ans = new double[5 * n + 1];
101-
for (int i = 0; i < ans.length; ++i) {
102-
ans[i] = f[n][n + i] / m;
101+
for (int j = n; j <= 6 * n; ++j) {
102+
ans[j - n] = f[n][j] / m;
103103
}
104104
return ans;
105105
}
@@ -126,10 +126,10 @@ public:
126126
}
127127
}
128128
}
129-
vector<double> ans(5 * n + 1);
129+
vector<double> ans;
130130
double m = pow(6, n);
131-
for (int i = 0; i < ans.size(); ++i) {
132-
ans[i] = f[n][n + i] / m;
131+
for (int j = n; j <= 6 * n; ++j) {
132+
ans.push_back(f[n][j] / m);
133133
}
134134
return ans;
135135
}
@@ -199,20 +199,29 @@ var dicesProbability = function (n) {
199199
```cs
200200
public class Solution {
201201
public double[] DicesProbability(int n) {
202-
var bp = new double[6];
203-
for (int i = 0; i < 6; i++) {
204-
bp[i] = 1 / 6.0;
202+
int[,] f = new int[n + 1, 6 * n + 1];
203+
204+
for (int j = 1; j <= 6; ++j) {
205+
f[1, j] = 1;
205206
}
206-
double[] ans = new double[]{1};
207-
for (int i = 1; i <= n; i++) {
208-
var tmp = ans;
209-
ans = new double[tmp.Length + 5];
210-
for (int i1 = 0; i1 < tmp.Length; i1++) {
211-
for (int i2 = 0; i2 < bp.Length; i2++) {
212-
ans[i1+i2] += tmp[i1] * bp[i2];
207+
208+
for (int i = 2; i <= n; ++i) {
209+
for (int j = i; j <= 6 * i; ++j) {
210+
for (int k = 1; k <= 6; ++k) {
211+
if (j >= k) {
212+
f[i, j] += f[i - 1, j - k];
213+
}
213214
}
214215
}
215216
}
217+
218+
double m = Math.Pow(6, n);
219+
double[] ans = new double[5 * n + 1];
220+
221+
for (int j = n; j <= 6 * n; ++j) {
222+
ans[j - n] = f[n, j] / m;
223+
}
224+
216225
return ans;
217226
}
218227
}
@@ -224,7 +233,9 @@ public class Solution {
224233

225234
<!-- solution:start-->
226235

227-
### 方法二
236+
### 方法二:动态规划(空间优化)
237+
238+
我们可以发现,上述方法中的 $f[i][j]$ 的值仅与 $f[i-1][j-k]$ 有关,因此我们可以使用滚动数组的方式,将空间复杂度优化至 $O(6n)$。
228239

229240
<!-- tabs:start -->
230241

@@ -245,25 +256,125 @@ class Solution:
245256
return [f[j] / m for j in range(n, 6 * n + 1)]
246257
```
247258

259+
#### Java
260+
261+
```java
262+
class Solution {
263+
public double[] dicesProbability(int n) {
264+
int[] f = new int[7];
265+
Arrays.fill(f, 1);
266+
f[0] = 0;
267+
for (int i = 2; i <= n; ++i) {
268+
int[] g = new int[6 * i + 1];
269+
for (int j = i; j <= 6 * i; ++j) {
270+
for (int k = 1; k <= 6; ++k) {
271+
if (j - k >= 0 && j - k < f.length) {
272+
g[j] += f[j - k];
273+
}
274+
}
275+
}
276+
f = g;
277+
}
278+
double m = Math.pow(6, n);
279+
double[] ans = new double[5 * n + 1];
280+
for (int j = n; j <= 6 * n; ++j) {
281+
ans[j - n] = f[j] / m;
282+
}
283+
return ans;
284+
}
285+
}
286+
```
287+
248288
#### Go
249289

250290
```go
251-
func dicesProbability(n int) []float64 {
252-
dp := make([]float64, 7)
291+
func dicesProbability(n int) (ans []float64) {
292+
f := make([]int, 7)
253293
for i := 1; i <= 6; i++ {
254-
dp[i] = 1.0 / 6.0
294+
f[i] = 1
255295
}
296+
256297
for i := 2; i <= n; i++ {
257-
n := len(dp)
258-
tmp := make([]float64, 6*i+1)
259-
for j := 0; j < n; j++ {
298+
g := make([]int, 6*i+1)
299+
for j := i; j <= 6*i; j++ {
260300
for k := 1; k <= 6; k++ {
261-
tmp[j+k] += dp[j] / 6.0
301+
if j-k >= 0 && j-k < len(f) {
302+
g[j] += f[j-k]
303+
}
262304
}
263305
}
264-
dp = tmp
306+
f = g
265307
}
266-
return dp[n:]
308+
309+
m := math.Pow(6, float64(n))
310+
for j := n; j <= 6*n; j++ {
311+
ans = append(ans, float64(f[j])/m)
312+
}
313+
return
314+
}
315+
```
316+
317+
#### JavaScript
318+
319+
```js
320+
/**
321+
* @param {number} num
322+
* @return {number[]}
323+
*/
324+
var dicesProbability = function (n) {
325+
let f = Array(7).fill(1);
326+
f[0] = 0;
327+
for (let i = 2; i <= n; ++i) {
328+
let g = Array(6 * i + 1).fill(0);
329+
for (let j = i; j <= 6 * i; ++j) {
330+
for (let k = 1; k <= 6; ++k) {
331+
if (j - k >= 0 && j - k < f.length) {
332+
g[j] += f[j - k];
333+
}
334+
}
335+
}
336+
f = g;
337+
}
338+
339+
const ans = [];
340+
const m = Math.pow(6, n);
341+
for (let j = n; j <= 6 * n; ++j) {
342+
ans.push(f[j] / m);
343+
}
344+
return ans;
345+
};
346+
```
347+
348+
#### C#
349+
350+
```cs
351+
public class Solution {
352+
public double[] DicesProbability(int n) {
353+
int[] f = new int[7];
354+
for (int i = 1; i <= 6; ++i) {
355+
f[i] = 1;
356+
}
357+
f[0] = 0;
358+
359+
for (int i = 2; i <= n; ++i) {
360+
int[] g = new int[6 * i + 1];
361+
for (int j = i; j <= 6 * i; ++j) {
362+
for (int k = 1; k <= 6; ++k) {
363+
if (j - k >= 0 && j - k < f.Length) {
364+
g[j] += f[j - k];
365+
}
366+
}
367+
}
368+
f = g;
369+
}
370+
371+
double m = Math.Pow(6, n);
372+
double[] ans = new double[5 * n + 1];
373+
for (int j = n; j <= 6 * n; ++j) {
374+
ans[j - n] = f[j] / m;
375+
}
376+
return ans;
377+
}
267378
}
268379
```
269380

lcof/面试题60. n个骰子的点数/Solution.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ class Solution {
1515
}
1616
}
1717
}
18-
vector<double> ans(5 * n + 1);
18+
vector<double> ans;
1919
double m = pow(6, n);
20-
for (int i = 0; i < ans.size(); ++i) {
21-
ans[i] = f[n][n + i] / m;
20+
for (int j = n; j <= 6 * n; ++j) {
21+
ans.push_back(f[n][j] / m);
2222
}
2323
return ans;
2424
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
public class Solution {
22
public double[] DicesProbability(int n) {
3-
var bp = new double[6];
4-
for (int i = 0; i < 6; i++) {
5-
bp[i] = 1 / 6.0;
3+
int[,] f = new int[n + 1, 6 * n + 1];
4+
5+
for (int j = 1; j <= 6; ++j) {
6+
f[1, j] = 1;
67
}
7-
double[] ans = new double[]{1};
8-
for (int i = 1; i <= n; i++) {
9-
var tmp = ans;
10-
ans = new double[tmp.Length + 5];
11-
for (int i1 = 0; i1 < tmp.Length; i1++) {
12-
for (int i2 = 0; i2 < bp.Length; i2++) {
13-
ans[i1+i2] += tmp[i1] * bp[i2];
8+
9+
for (int i = 2; i <= n; ++i) {
10+
for (int j = i; j <= 6 * i; ++j) {
11+
for (int k = 1; k <= 6; ++k) {
12+
if (j >= k) {
13+
f[i, j] += f[i - 1, j - k];
14+
}
1415
}
1516
}
1617
}
18+
19+
double m = Math.Pow(6, n);
20+
double[] ans = new double[5 * n + 1];
21+
22+
for (int j = n; j <= 6 * n; ++j) {
23+
ans[j - n] = f[n, j] / m;
24+
}
25+
1726
return ans;
1827
}
19-
}
28+
}

lcof/面试题60. n个骰子的点数/Solution.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public double[] dicesProbability(int n) {
1515
}
1616
double m = Math.pow(6, n);
1717
double[] ans = new double[5 * n + 1];
18-
for (int i = 0; i < ans.length; ++i) {
19-
ans[i] = f[n][n + i] / m;
18+
for (int j = n; j <= 6 * n; ++j) {
19+
ans[j - n] = f[n][j] / m;
2020
}
2121
return ans;
2222
}

lcof/面试题60. n个骰子的点数/Solution.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ def dicesProbability(self, n: int) -> List[float]:
99
if j - k >= 0:
1010
f[i][j] += f[i - 1][j - k]
1111
m = pow(6, n)
12-
return [f[n][i] / m for i in range(n, 6 * n + 1)]
12+
return [f[n][j] / m for j in range(n, 6 * n + 1)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Solution {
2+
public:
3+
vector<double> dicesProbability(int n) {
4+
vector<int> f(7, 1);
5+
f[0] = 0;
6+
for (int i = 2; i <= n; ++i) {
7+
vector<int> g(6 * i + 1, 0);
8+
for (int j = i; j <= 6 * i; ++j) {
9+
for (int k = 1; k <= 6; ++k) {
10+
if (j - k >= 0 && j - k < f.size()) {
11+
g[j] += f[j - k];
12+
}
13+
}
14+
}
15+
f = move(g);
16+
}
17+
double m = pow(6, n);
18+
vector<double> ans;
19+
for (int j = n; j <= 6 * n; ++j) {
20+
ans.push_back(f[j] / m);
21+
}
22+
return ans;
23+
}
24+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
public class Solution {
2+
public double[] DicesProbability(int n) {
3+
int[] f = new int[7];
4+
for (int i = 1; i <= 6; ++i) {
5+
f[i] = 1;
6+
}
7+
f[0] = 0;
8+
9+
for (int i = 2; i <= n; ++i) {
10+
int[] g = new int[6 * i + 1];
11+
for (int j = i; j <= 6 * i; ++j) {
12+
for (int k = 1; k <= 6; ++k) {
13+
if (j - k >= 0 && j - k < f.Length) {
14+
g[j] += f[j - k];
15+
}
16+
}
17+
}
18+
f = g;
19+
}
20+
21+
double m = Math.Pow(6, n);
22+
double[] ans = new double[5 * n + 1];
23+
for (int j = n; j <= 6 * n; ++j) {
24+
ans[j - n] = f[j] / m;
25+
}
26+
return ans;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
func dicesProbability(n int) []float64 {
2-
dp := make([]float64, 7)
1+
func dicesProbability(n int) (ans []float64) {
2+
f := make([]int, 7)
33
for i := 1; i <= 6; i++ {
4-
dp[i] = 1.0 / 6.0
4+
f[i] = 1
55
}
6+
67
for i := 2; i <= n; i++ {
7-
n := len(dp)
8-
tmp := make([]float64, 6*i+1)
9-
for j := 0; j < n; j++ {
8+
g := make([]int, 6*i+1)
9+
for j := i; j <= 6*i; j++ {
1010
for k := 1; k <= 6; k++ {
11-
tmp[j+k] += dp[j] / 6.0
11+
if j-k >= 0 && j-k < len(f) {
12+
g[j] += f[j-k]
13+
}
1214
}
1315
}
14-
dp = tmp
16+
f = g
1517
}
16-
return dp[n:]
18+
19+
m := math.Pow(6, float64(n))
20+
for j := n; j <= 6*n; j++ {
21+
ans = append(ans, float64(f[j])/m)
22+
}
23+
return
1724
}

0 commit comments

Comments
 (0)