剑指offer-43.n个骰子的点数

https://www.lintcode.com/problem/dices-sum/description

描述
扔 n 个骰子,向上面的数字之和为 S。给定 Given n,请列出所有可能的 S 值及其相应的概率。

样例
给定 n = 1,返回 [ [1, 0.17], [2, 0.17], [3, 0.17], [4, 0.17], [5, 0.17], [6, 0.17]]。

题解:
方法一:

所有可能的S值为 全为1 到 全为6,即 n到 6*n 。
用数组int[] p = new int[ 6*n- n + 1]; 存放每个可能点数出现的次数。
深度搜索所有筛子可能投出的点数,记录他们的和,累加相应的点数的次数。
最后每个次数,除以总的可能数 6^n ,即为每个可能的概率。

此种方法属于暴力,会超时。

class Solution {

    public List> dicesSum(int n) {
        List> result = new ArrayList<>();
        if (n < 1) {
            return result;
        }
        int maxSum = n * 6;// 最大的大小
        int[] p = new int[maxSum - n + 1];// 存放每个可能点数和出现的次数,从 n 到maxSum 一共maxSum-n+1个可能
        probability(n, n, 0, p);
        int total = (int) Math.pow(6, n); // 总的可能数
        for (int i = 0; i < p.length; i++) {
            result.add( new AbstractMap.SimpleEntry(i + n, p[i]*1.0 / total) );
        }
        return result;
    }

    //筛子个数,当前深度,点数总和,每个点数出现次数的数组
    private void probability(int original, int current, int sum, int[] p) {
        if (current == 0) {
            p[sum - original]++;
        } else {
            for (int i = 1; i <= 6; i++) {
                probability(original, current - 1, i + sum, p);
            }
        }
    }
}

方法二:动态规划解法
使用一个二维数组 dp 存储点数出现的次数,其中 dp[i][j] 表示前 i 个骰子产生点数 j 的次数。

dp[i][j] += dp[i - 1][j - k];
和为 j 的骰子出现的次数等于上一次循环中骰子点数和为 j-1,j-2,j-3, 到 j-k 的次数的总和。

空间复杂度: O(N2) O ( N 2 )

class Solution {
    public List> dicesSum(int n) {
        final int face = 6;
        final int pointNum = face * n;
        long[][] dp = new long[n + 1][pointNum + 1]; // dp[i][j]表示前i个骰子产生点数 j的次数

        for (int i = 1; i <= face; i++){
            dp[1][i] = 1;
        }

        for (int i = 2; i <= n; i++)
            for (int j = i; j <= pointNum; j++) // 使用 i 个骰子最小点数为 i
                for (int k = 1; k <= face && k <= j; k++)
                    dp[i][j] += dp[i - 1][j - k];

        final double totalNum = Math.pow(6, n);
        List> ret = new ArrayList<>();
        for (int i = n; i <= pointNum; i++){
            ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum));
        }
        return ret;
    }

}

方法三:动态规划解法 + 旋转数组
空间复杂度:O(N)

public List> dicesSum(int n) {
    final int face = 6;
    final int pointNum = face * n;
    long[][] dp = new long[2][pointNum + 1];

    for (int i = 1; i <= face; i++)
        dp[0][i] = 1;

    int flag = 1;                                     /* 旋转标记 */
    for (int i = 2; i <= n; i++, flag = 1 - flag) {
        for (int j = 0; j <= pointNum; j++)
            dp[flag][j] = 0;                          /* 旋转数组清零 */

        for (int j = i; j <= pointNum; j++)
            for (int k = 1; k <= face && k <= j; k++)
                dp[flag][j] += dp[1 - flag][j - k];
    }

    final double totalNum = Math.pow(6, n);
    List> ret = new ArrayList<>();
    for (int i = n; i <= pointNum; i++)
        ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum));

    return ret;
}

你可能感兴趣的:(剑指offer,剑指offer题解)