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

在这里插入图片描述

解法一 递归

class Solution {
    // 提高程序扩展性
    public int maxValue = 6;
    public double[] twoSum(int n) {
        if (n < 1) return new double[]{};
        // 最大和为6n,最小和为n,可能的和有 6n - n + 1 种
        int maxSum = n * maxValue;
        // 定义一个长度为6n - n + 1的数组,每个元素代表每个可能的和s的出现次数
        int[] probabilities = new int[maxSum - n + 1];

        probability(n, probabilities);
        
        double[] ans = new double[probabilities.length];
        // 由排列组合可知n个骰子排列数次数为6^n
        double total = Math.pow(maxValue, n);
        for (int i = n; i <= maxSum ; i++) {
            ans[i - n] = (double)probabilities[i - n] / total;
        }
        return ans;
    }

    private void probability(int n, int[] probabilities) {
        // 把n个骰子分成两部分:第一部分一个,有1-6种;第二部分n-1个。
        // 计算1-6中每个点数与剩下n-1个骰子的点数和
        for (int i = 1; i <= maxValue; i++) {
            probability(n, n, i, probabilities);
        }
    }

    // current 为当前剩余的骰子数; sum为当前的和
    private void probability(int n, int current, int sum, int[] probabilities) {
        if (current == 1) {
            probabilities[sum - n]++;
        } else {
            for (int i = 1; i <= maxValue; i++) {
                probability(n, current - 1, sum + i, probabilities);
            }
        }
    }
}

解法二 动态规化

class Solution {
    // 提高程序扩展性
    public int maxValue = 6;
    public double[] twoSum(int n) {
        if (n < 1) return new double[]{};
        // 最大和为6n,最小和为n,可能的和有 6n - n + 1 种
        int maxSum = n * maxValue;

        int[][] probablilities = new int[2][maxSum + 1];
        Arrays.fill(probablilities[0], 0);
        Arrays.fill(probablilities[1], 0);
        int flag = 0;

        // 第一轮循环:只有一个骰子时1~6每个数字出现的次数为1
        for (int i = 1; i <= maxValue; i++) 
            probablilities[flag][i] = 1;
        // 第二轮循环: 每次增加一个骰子,范围为2~n
        for (int k  = 2; k <= n; k++) {
            // 另一个数组和为前k-1为的数字出现次数为0
            for (int i = 0; i < k; ++i) 
                probablilities[1-flag][i] = 0;
            // 计算k~maxValue * k之间每个和出现的次数
            for (int i = k; i <= maxValue * k; i++) {
                // 先初始化为0
                probablilities[1-flag][i] = 0;
                for (int j = 1; j <= i && j <= maxValue; j++) {
                    probablilities[1-flag][i] += probablilities[flag][i-j];
                }
            }
            // 交换数组
            flag = 1-flag;
        }
        double total = Math.pow(maxValue, n);
        double[] ans = new double[maxSum - n + 1];
        for (int i = n; i <= maxSum; i++) {
            ans[i-n] = probablilities[flag][i]/total;
        }
        return ans;
    }
}
posted @ 2020-09-24 21:29  消灭猕猴桃  阅读(69)  评论(0编辑  收藏  举报