Skip to content

Commit c9a0ef0

Browse files
committed
feat: add solutions to lcof problem: No.60
1 parent d6659a9 commit c9a0ef0

File tree

6 files changed

+201
-97
lines changed

6 files changed

+201
-97
lines changed

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

+122-53
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,21 @@
3333

3434
<!-- 这里可写通用的实现逻辑 -->
3535

36-
动态规划求解。
36+
**方法一:动态规划**
3737

38-
扔 n 个骰子,点数之和的范围在 `[n, 6n]` 之间,总共有 `5n+1` 种,即为最后结果数组的长度。
38+
我们定义 $f[i][j]$ 表示投掷 $i$ 个骰子,点数和为 $j$ 的方案数。那么我们可以写出状态转移方程:
3939

40-
假设 `dp[i][j]` 表示扔 i 个骰子,出现点数之和 j 的次数。n 个骰子,所以 i 的范围在 `1~n`,j 的范围在 `[1, 6n]`
40+
$$
41+
f[i][j] = \sum_{k=1}^6 f[i-1][j-k]
42+
$$
4143

42-
单看第 n 枚骰子,它的点数可能为 `1,2,3,...,6`,因此扔完 n 枚骰子后点数之和 j 出现的次数,可以由扔完 n-1 枚骰子后,对应点数 `j−1,j−2,j−3,...,j−6` 出现的次数之和转化过来。即:
44+
其中 $k$ 表示当前骰子的点数,$f[i-1][j-k]$ 表示投掷 $i-1$ 个骰子,点数和为 $j-k$ 的方案数。
4345

44-
```
45-
for (第n枚骰子的点数 i = 1; i <= 6; i ++) {
46-
dp[n][j] += dp[n-1][j - i]
47-
}
48-
```
46+
最终我们需要求的是 $f[n][n \sim 6n]$ 的和,即投掷 $n$ 个骰子,点数和为 $n \sim 6n$ 的方案数之和。
4947

50-
扔 1 枚骰子,点数可能是 `1,2,3,4,5,6`,且每个点数出现的次数均为 1。所以初始化如下:
48+
注意到 $f[i][j]$ 的值只与 $f[i-1][j-k]$ 有关,因此我们可以使用滚动数组的方法将空间复杂度降低到 $O(6n)$。
5149

52-
```
53-
for (int j = 1; j <= 6; ++j) {
54-
dp[1][j] = 1;
55-
}
56-
```
50+
时间复杂度 $O(n^2)$,空间复杂度 $O(6n)$。其中 $n$ 为骰子个数。
5751

5852
<!-- tabs:start -->
5953

@@ -63,20 +57,32 @@ for (int j = 1; j <= 6; ++j) {
6357

6458
```python
6559
class Solution:
66-
def twoSum(self, n: int) -> List[float]:
67-
dp = [[0 for _ in range(6 * n + 1)] for _ in range(n + 1)]
60+
def dicesProbability(self, n: int) -> List[float]:
61+
f = [[0] * (6 * n + 1) for _ in range(n + 1)]
6862
for j in range(1, 7):
69-
dp[1][j] = 1
63+
f[1][j] = 1
64+
for i in range(2, n + 1):
65+
for j in range(i, 6 * i + 1):
66+
for k in range(1, 7):
67+
if j - k >= 0:
68+
f[i][j] += f[i - 1][j - k]
69+
m = pow(6, n)
70+
return [f[n][i] / m for i in range(n, 6 * n + 1)]
71+
```
72+
73+
```python
74+
class Solution:
75+
def dicesProbability(self, n: int) -> List[float]:
76+
f = [0] + [1] * 6
7077
for i in range(2, n + 1):
78+
g = [0] * (6 * i + 1)
7179
for j in range(i, 6 * i + 1):
7280
for k in range(1, 7):
73-
if j <= k:
74-
break
75-
dp[i][j] += dp[i - 1][j - k]
76-
res, total = [], pow(6, n)
77-
for i in range(5 * n + 1):
78-
res.append(dp[n][n + i] / total)
79-
return res
81+
if 0 <= j - k < len(f):
82+
g[j] += f[j - k]
83+
f = g
84+
m = pow(6, n)
85+
return [f[j] / m for j in range(n, 6 * n + 1)]
8086
```
8187

8288
### **Java**
@@ -85,55 +91,88 @@ class Solution:
8591

8692
```java
8793
class Solution {
88-
public double[] twoSum(int n) {
89-
int[][] dp = new int[n + 1][6 * n + 1];
94+
public double[] dicesProbability(int n) {
95+
int[][] f = new int[n + 1][6 * n + 1];
9096
for (int j = 1; j <= 6; ++j) {
91-
dp[1][j] = 1;
97+
f[1][j] = 1;
9298
}
9399
for (int i = 2; i <= n; ++i) {
94100
for (int j = i; j <= 6 * i; ++j) {
95-
for (int k = 1; k <= 6 && j > k; ++k) {
96-
dp[i][j] += dp[i - 1][j - k];
101+
for (int k = 1; k <= 6; ++k) {
102+
if (j >= k) {
103+
f[i][j] += f[i - 1][j - k];
104+
}
97105
}
98106
}
99107
}
100-
double[] res = new double[5 * n + 1];
101-
double all = Math.pow(6, n);
102-
for (int i = 0; i <= 5 * n; ++i) {
103-
res[i] = dp[n][n + i] * 1.0 / all;
108+
double m = Math.pow(6, n);
109+
double[] ans = new double[5 * n + 1];
110+
for (int i = 0; i < ans.length; ++i) {
111+
ans[i] = f[n][n + i] / m;
104112
}
105-
return res;
113+
return ans;
106114
}
107115
}
108116
```
109117

110-
### **JavaScript**
118+
### **C++**
111119

112-
```js
113-
/**
114-
* @param {number} n
115-
* @return {number[]}
116-
*/
117-
var twoSum = function (n) {
118-
function backtrack(sum, time) {
119-
if (time === n) {
120-
res[sum]++;
121-
return;
120+
```cpp
121+
class Solution {
122+
public:
123+
vector<double> dicesProbability(int n) {
124+
int f[n + 1][6 * n + 1];
125+
memset(f, 0, sizeof f);
126+
for (int j = 1; j <= 6; ++j) {
127+
f[1][j] = 1;
122128
}
123-
for (let i = 1; i <= 6; i++) {
124-
backtrack(sum + i, time + 1);
129+
for (int i = 2; i <= n; ++i) {
130+
for (int j = i; j <= 6 * i; ++j) {
131+
for (int k = 1; k <= 6; ++k) {
132+
if (j >= k) {
133+
f[i][j] += f[i - 1][j - k];
134+
}
135+
}
136+
}
137+
}
138+
vector<double> ans(5 * n + 1);
139+
double m = pow(6, n);
140+
for (int i = 0; i < ans.size(); ++i) {
141+
ans[i] = f[n][n + i] / m;
125142
}
143+
return ans;
126144
}
127-
let len = n * 6;
128-
let t = 6 ** n;
129-
let res = new Array(len + 1).fill(0);
130-
backtrack(0, 0);
131-
return res.slice(n).map(e => e / t);
132145
};
133146
```
134147
135148
### **Go**
136149
150+
```go
151+
func dicesProbability(n int) (ans []float64) {
152+
f := make([][]int, n+1)
153+
for i := range f {
154+
f[i] = make([]int, 6*n+1)
155+
}
156+
for j := 1; j <= 6; j++ {
157+
f[1][j] = 1
158+
}
159+
for i := 2; i <= n; i++ {
160+
for j := i; j <= 6*i; j++ {
161+
for k := 1; k <= 6; k++ {
162+
if j >= k {
163+
f[i][j] += f[i-1][j-k]
164+
}
165+
}
166+
}
167+
}
168+
m := math.Pow(6, float64(n))
169+
for j := n; j <= 6*n; j++ {
170+
ans = append(ans, float64(f[n][j])/m)
171+
}
172+
return
173+
}
174+
```
175+
137176
```go
138177
func dicesProbability(n int) []float64 {
139178
dp := make([]float64, 7)
@@ -154,6 +193,36 @@ func dicesProbability(n int) []float64 {
154193
}
155194
```
156195

196+
### **JavaScript**
197+
198+
```js
199+
/**
200+
* @param {number} n
201+
* @return {number[]}
202+
*/
203+
var dicesProbability = function (n) {
204+
const f = Array.from({ length: n + 1 }, () => Array(6 * n + 1).fill(0));
205+
for (let j = 1; j <= 6; ++j) {
206+
f[1][j] = 1;
207+
}
208+
for (let i = 2; i <= n; ++i) {
209+
for (let j = i; j <= 6 * i; ++j) {
210+
for (let k = 1; k <= 6; ++k) {
211+
if (j >= k) {
212+
f[i][j] += f[i - 1][j - k];
213+
}
214+
}
215+
}
216+
}
217+
const ans = [];
218+
const m = Math.pow(6, n);
219+
for (let j = n; j <= 6 * n; ++j) {
220+
ans.push(f[n][j] / m);
221+
}
222+
return ans;
223+
};
224+
```
225+
157226
### **C#**
158227

159228
```cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class Solution {
2+
public:
3+
vector<double> dicesProbability(int n) {
4+
int f[n + 1][6 * n + 1];
5+
memset(f, 0, sizeof f);
6+
for (int j = 1; j <= 6; ++j) {
7+
f[1][j] = 1;
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+
}
15+
}
16+
}
17+
}
18+
vector<double> ans(5 * n + 1);
19+
double m = pow(6, n);
20+
for (int i = 0; i < ans.size(); ++i) {
21+
ans[i] = f[n][n + i] / m;
22+
}
23+
return ans;
24+
}
25+
};
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1-
func dicesProbability(n int) []float64 {
2-
dp := make([]float64, 7)
3-
for i := 1; i <= 6; i++ {
4-
dp[i] = 1.0 / 6.0
1+
func dicesProbability(n int) (ans []float64) {
2+
f := make([][]int, n+1)
3+
for i := range f {
4+
f[i] = make([]int, 6*n+1)
5+
}
6+
for j := 1; j <= 6; j++ {
7+
f[1][j] = 1
58
}
69
for i := 2; i <= n; i++ {
7-
n := len(dp)
8-
tmp := make([]float64, 6*i+1)
9-
for j := 0; j < n; j++ {
10+
for j := i; j <= 6*i; j++ {
1011
for k := 1; k <= 6; k++ {
11-
tmp[j+k] += dp[j] / 6.0
12+
if j >= k {
13+
f[i][j] += f[i-1][j-k]
14+
}
1215
}
1316
}
14-
dp = tmp
1517
}
16-
return dp[n:]
17-
}
18+
m := math.Pow(6, float64(n))
19+
for j := n; j <= 6*n; j++ {
20+
ans = append(ans, float64(f[n][j])/m)
21+
}
22+
return
23+
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
class Solution {
2-
public double[] twoSum(int n) {
3-
int[][] dp = new int[n + 1][6 * n + 1];
2+
public double[] dicesProbability(int n) {
3+
int[][] f = new int[n + 1][6 * n + 1];
44
for (int j = 1; j <= 6; ++j) {
5-
dp[1][j] = 1;
5+
f[1][j] = 1;
66
}
77
for (int i = 2; i <= n; ++i) {
88
for (int j = i; j <= 6 * i; ++j) {
9-
for (int k = 1; k <= 6 && j > k; ++k) {
10-
dp[i][j] += dp[i - 1][j - k];
9+
for (int k = 1; k <= 6; ++k) {
10+
if (j >= k) {
11+
f[i][j] += f[i - 1][j - k];
12+
}
1113
}
1214
}
1315
}
14-
double[] res = new double[5 * n + 1];
15-
double all = Math.pow(6, n);
16-
for (int i = 0; i <= 5 * n; ++i) {
17-
res[i] = dp[n][n + i] * 1.0 / all;
16+
double m = Math.pow(6, n);
17+
double[] ans = new double[5 * n + 1];
18+
for (int i = 0; i < ans.length; ++i) {
19+
ans[i] = f[n][n + i] / m;
1820
}
19-
return res;
21+
return ans;
2022
}
2123
}

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

+18-13
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22
* @param {number} n
33
* @return {number[]}
44
*/
5-
var twoSum = function (n) {
6-
function backtrack(sum, time) {
7-
if (time === n) {
8-
res[sum]++;
9-
return;
10-
}
11-
for (let i = 1; i <= 6; i++) {
12-
backtrack(sum + i, time + 1);
5+
var dicesProbability = function (n) {
6+
const f = Array.from({ length: n + 1 }, () => Array(6 * n + 1).fill(0));
7+
for (let j = 1; j <= 6; ++j) {
8+
f[1][j] = 1;
9+
}
10+
for (let i = 2; i <= n; ++i) {
11+
for (let j = i; j <= 6 * i; ++j) {
12+
for (let k = 1; k <= 6; ++k) {
13+
if (j >= k) {
14+
f[i][j] += f[i - 1][j - k];
15+
}
16+
}
1317
}
1418
}
15-
let len = n * 6;
16-
let t = 6 ** n;
17-
let res = new Array(len + 1).fill(0);
18-
backtrack(0, 0);
19-
return res.slice(n).map(e => e / t);
19+
const ans = [];
20+
const m = Math.pow(6, n);
21+
for (let j = n; j <= 6 * n; ++j) {
22+
ans.push(f[n][j] / m);
23+
}
24+
return ans;
2025
};
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
class Solution:
2-
def twoSum(self, n: int) -> List[float]:
3-
dp = [[0 for _ in range(6 * n + 1)] for _ in range(n + 1)]
2+
def dicesProbability(self, n: int) -> List[float]:
3+
f = [[0] * (6 * n + 1) for _ in range(n + 1)]
44
for j in range(1, 7):
5-
dp[1][j] = 1
5+
f[1][j] = 1
66
for i in range(2, n + 1):
77
for j in range(i, 6 * i + 1):
88
for k in range(1, 7):
9-
if j <= k:
10-
break
11-
dp[i][j] += dp[i - 1][j - k]
12-
res, total = [], pow(6, n)
13-
for i in range(5 * n + 1):
14-
res.append(dp[n][n + i] / total)
15-
return res
9+
if j - k >= 0:
10+
f[i][j] += f[i - 1][j - k]
11+
m = pow(6, n)
12+
return [f[n][i] / m for i in range(n, 6 * n + 1)]

0 commit comments

Comments
 (0)