这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战
n个骰子的点数
题解
方法一:动态规划——Java
投掷 n 个骰子,所有点数出现的总次数是 6^n,因为一共有 n 枚骰子,每枚骰子的点数都有 6 种可能出现的情况。
我们的目的就是 计算出投掷完 n 枚骰子后每个点数出现的次数。
假设已知 n - 1 个骰子的解 f(n - 1) ,此时添加一枚骰子,求 n 个骰子的点数和为 x 的概率 f(n, x) 。
当添加骰子的点数为 1 时,前 n - 1n−1 个骰子的点数和应为 x - 1 ,方可组成点数和 x ;同理,当此骰子为 22 时,前 n - 1 个骰子应为 x - 2 ;以此类推,直至此骰子点数为 6 。将这 6 种情况的概率相加,即可得到概率 f(n, x) 。递推公式如下所示:
f(n,x) = i=1∑6 f(n−1,x−i) × 1/6
通常做法是声明一个二维数组 dp ,dp[i][j] 代表前 i 个骰子的点数和 j 的概率,并执行状态转移。而由于 dp[i] 仅由 dp[i-1] 递推得出,为降低空间复杂度,只建立两个一维数组 dp , tmp 交替前进即可。
class Solution {
public double[] dicesProbability(int n) {
if (n <= 0) {
return new double[0];
}
double[] dp = new double[70];
// 初始化第一个骰子
for (int i = 1; i <= 6; i++) {
dp[i] = 1;
}
// 从第二个开始
for (int i = 2; i <= n; i++) {
for (int j = 6 * n; 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 num = Math.pow(6, n);
double[] res = new double[(n * 6) - n + 1];
for (int i = 0, j = n; i < (n * 6) - n + 1; i++, j++) {
res[i] = dp[j] / num;
}
return res;
}
}
复制代码
时间复杂度:O(n^2)
空间复杂度:O(n)
方法一:动态规划——Go
func dicesProbability(n int) []float64 {
dp := make([][]float64, n)
for i := range dp{
dp[i] = make([]float64, (i + 1) * 6 - i)
}
for i := range dp[0]{
dp[0][i] = float64(1) / float64(6)
}
for i := 1; i < len(dp); i ++{
for j := range dp[i - 1]{
for k := range dp[0]{
dp[i][j + k] += float64(dp[i - 1][j]) * float64(dp[0][k])
}
}
}
return dp[n - 1]
}
复制代码