Go&Java算法之n个骰子的点数

这是我参与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]
}
复制代码

猜你喜欢

转载自juejin.im/post/7035614074104709127