剑指 Offer 60. n个骰子的点数

题目描述

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。

输入输出

1
2
3
4
5
输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

基本思路

  1. 暴力破解 输入n 列出s所有可能[n, 6n] 一共(5n+1)种可能 最后按照概率相除 时间复杂度 $O(6^n)$ 爆炸

  2. 动态规划

    1. 设置dp[i][j]为投完i枚骰子之后点数j出现的次数 得状态转移方程如下:$dp[n][j]=\sum_{i=1}^{6} d p[n-1][j-i]$

    2. 边界处理:第一阶段的状态:投掷完 1 枚骰子后,它的可能点数分别为 1, 2, 3, … , 6,并且每个点数出现的次数都是 1

    1
    2
    3
    for (int i = 1; i <= 6; i++) {
    dp[1][i] = 1;
    }
  1. 动态规划的空间优化(未知)

java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 普通动态规划
class Solution {
public double[] dicesProbability(int n) {
int[][] dp = new int[n+1][6*n+1];
for(int i = 1; i <= 6; i++){
dp[1][i] = 1;
}
for(int i = 2; i <= n; i++){
for(int j = i; j <= 6*i; j++){
for(int k = 1; k<=j&&k<=6; k++){
dp[i][j] += dp[i - 1][j - k];
}
}
}
double[] ans = new double[5*n+1];
for(int i = n; i <= 6*n; i++){
ans[i-n] = ((double)dp[n][i] / Math.pow(6,n));
}
return ans;

}
}

// 优化空间复杂度 还不是很理解
class Solution {
public double[] dicesProbability(int n) {
int[] dp = new int[6*n+1];
double all = Math.pow(6, n);
for(int i = 1; i <= 6; i++){
dp[i] = 1;
}
for(int i = 2; i <= n; i++){
for(int j = i*6; j >= i; j--){
dp[j] = 0;
for(int k = 1; k <= 6; k++){
if(j-k<i-1) break;
dp[j] += dp[j-k];
}
}
}
double[] ans = new double[5*n+1];
for(int i = n; i <= 6*n; i++){
ans[i-n] = dp[i] / all;
}
return ans;
}
}