51
51
f[i][j] = \sum_{k=1}^6 f[i-1][j-k]
52
52
$$
53
53
54
- 其中 $k$ 表示当前骰子的点数,$f[ i-1] [ j-k ] $ 表示投掷 $i-1$ 个骰子,点数和为 $j-k$ 的方案数。
54
+ 其中 $k$ 表示当前骰子的点数,而 $f[ i-1] [ j-k ] $ 表示投掷 $i-1$ 个骰子,点数和为 $j-k$ 的方案数。
55
55
56
- 最终我们需要求的是 $f[ n ] [ n \sim 6n ] $ 的和,即投掷 $n$ 个骰子, 点数和为 $n \sim 6n$ 的方案数之和 。
56
+ 初始条件为 $f[ 1 ] [ j ] = 1$,表示投掷一个骰子, 点数和为 $j$ 的方案数为 $1$ 。
57
57
58
- 注意到 $f [ i ] [ j ] $ 的值只与 $f [ i-1 ] [ j-k ] $ 有关,因此我们可以使用滚动数组的方法将空间复杂度降低到 $O(6n) $。
58
+ 最终,我们要求的答案即为 $\frac{f [ n ] [ j ] }{6^n}$,其中 $n$ 为骰子个数,而 $j$ 的取值范围为 $ [ n, 6n ] $。
59
59
60
60
时间复杂度 $O(n^2)$,空间复杂度 $O(6n)$。其中 $n$ 为骰子个数。
61
61
@@ -75,7 +75,7 @@ class Solution:
75
75
if j - k >= 0 :
76
76
f[i][j] += f[i - 1 ][j - k]
77
77
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 )]
79
79
```
80
80
81
81
#### Java
@@ -98,8 +98,8 @@ class Solution {
98
98
}
99
99
double m = Math . pow(6 , n);
100
100
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;
103
103
}
104
104
return ans;
105
105
}
@@ -126,10 +126,10 @@ public:
126
126
}
127
127
}
128
128
}
129
- vector<double > ans(5 * n + 1) ;
129
+ vector<double > ans;
130
130
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) ;
133
133
}
134
134
return ans;
135
135
}
@@ -199,20 +199,29 @@ var dicesProbability = function (n) {
199
199
``` cs
200
200
public class Solution {
201
201
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 ;
205
206
}
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
+ }
213
214
}
214
215
}
215
216
}
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
+
216
225
return ans ;
217
226
}
218
227
}
@@ -224,7 +233,9 @@ public class Solution {
224
233
225
234
<!-- solution: start-- >
226
235
227
- ### 方法二
236
+ ### 方法二:动态规划(空间优化)
237
+
238
+ 我们可以发现,上述方法中的 $f[ i] [ j ] $ 的值仅与 $f[ i-1] [ j-k ] $ 有关,因此我们可以使用滚动数组的方式,将空间复杂度优化至 $O(6n)$。
228
239
229
240
<!-- tabs: start -->
230
241
@@ -245,25 +256,125 @@ class Solution:
245
256
return [f[j] / m for j in range (n, 6 * n + 1 )]
246
257
```
247
258
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
+
248
288
#### Go
249
289
250
290
``` go
251
- func dicesProbability (n int ) []float64 {
252
- dp := make ([]float64 , 7 )
291
+ func dicesProbability (n int ) ( ans []float64 ) {
292
+ f := make ([]int , 7 )
253
293
for i := 1 ; i <= 6 ; i++ {
254
- dp [i] = 1.0 / 6.0
294
+ f [i] = 1
255
295
}
296
+
256
297
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++ {
260
300
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
+ }
262
304
}
263
305
}
264
- dp = tmp
306
+ f = g
265
307
}
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
+ }
267
378
}
268
379
```
269
380
0 commit comments