题目
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。
解题思路
这题很复杂, 需要先推导出他的的容量, 一个骰子的时候只有6个几率,但是i=3个骰子时,最小就是3了,不可能是2和1,所以点数之和的值的个数是6i-(i-1),化简:5i+1,然后我们需要总结归纳出他每个概率是如何得出的,这里的n的概率公式可以看下面的代码注释, 具体代码实现如下。
Java代码实现
import java.util.Arrays;
public class DicesProbability {
/*
状态定义:dp[i]为投掷i个骰子各个点数的概率
状态转移:dp[1] = {1/6,1/6,1/6,1/6,1/6,1/6}
dp[2][0] = dp[1][0] / 6 = 1/6/6
dp[2][1] = dp[1][0] / 6 + dp[1][1] / 6 = 1/6/6 + 1/6/6
dp[2][2] = dp[1][0] / 6 + dp[1][1] / 6 + dp[1][2] / 6 = 1/6/6 + 1/6/6 + 1/6/6
dp[2][3] = dp[1][0] / 6 + dp[1][1] / 6 + dp[1][2] / 6 + dp[1][3] / 6 = 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6
dp[2][4] = dp[1][0] / 6 + dp[1][1] / 6 + dp[1][2] / 6 + dp[1][3] / 6 + dp[1][4] / 6 = 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6
dp[2][5] = dp[1][0] / 6 + dp[1][1] / 6 + dp[1][2] / 6 + dp[1][3] / 6 + dp[1][4] / 6 = 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6
dp[2][6] = dp[1][1] / 6 + dp[1][2] / 6 + dp[1][3] / 6 + dp[1][4] / 6 = 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6
dp[2][7] = dp[1][2] / 6 + dp[1][3] / 6 + dp[1][4] / 6 = 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6
dp[2][8] = dp[1][3] / 6 + dp[1][4] / 6 = 1/6/6 + 1/6/6 + 1/6/6 + 1/6/6
dp[2][9] = dp[1][4] / 6 + dp[1][5] / 6 = 1/6/6 + 1/6/6
dp[2][10] = dp[1][5] = 1/6/6
dp[3][0] = dp[2][0] /6
dp[3][1] = dp[2][0] /6 + dp[2][1] /6
dp[3][2] = dp[2][0] /6 + dp[2][1] /6 + dp[2][2] /6
...
1.如果n小于等于0,直接返回一个空数组。
2.初始化一个长度为6的数组,表示只有一个骰子时每个点数出现的概率,初始值为1/6。
3.从两个骰子开始遍历,每次遍历都根据上一次的结果计算出当前骰子的每个点数出现的概率。
4.对于当前遍历到的i个骰子,我们定义一个长度为5*i+1的数组tmp,其中tmp[j]表示点数j出现的概率。遍历上一个结果数组dp,对于每个dp[k]不为0的位置,都将当前骰子可能出现的6个点数的概率加到对应的tmp[k+j]上。
5.遍历完上一个数组dp后,将tmp数组作为新的结果数组dp,继续遍历下一个骰子。
6.遍历完n个骰子后,dp数组中每个位置的值就表示该点数出现的概率。返回dp数组即可。
*/
public double[] dicesProbability(int n) {
if (n <= 0) {
return new double[0];
}
// 初始化一个长度为6的数组,表示只有一个骰子时每个点数出现的概率
double[] dp = new double[6];
Arrays.fill(dp, 1.0 / 6.0);
// 从两个骰子开始遍历
for (int i = 2; i <= n; i++) {
// 新建一个数组,长度为5*i+1,表示i个骰子时每个点数出现的概率
double[] tmp = new double[5*i + 1];
// 遍历上一个数组
for (int k = 0; k < dp.length; k++) {
// 遍历当前骰子的6个可能点数
for (int j = 0; j < 6 ; j++) {
// 根据转移方程更新当前点数出现的概率
tmp[k + j] += dp[k]/6.0;
}
}
// 将更新后的数组作为下一个骰子的结果数组
dp = tmp;
}
return dp;
}
}