算法系列——n个骰子的点数

题目描述

把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s。输入 n,打印出 s 的所有可能的值出现的概率。

解题思路

此题目解法很多,其中使用动态规划法最好理解,代码也比较简洁

1.现在变量有:骰子个数,点数和。当有c个骰子,点数和为k时,出现次数记为dp(c,k)。那与c-1个骰子阶段之间的关系是怎样的?
2.当我有c-1个骰子时,再增加一个骰子,这个骰子的点数只可能为1、2、3、4、5或6。那k个骰子得到点数和为n的情况有:

(c-1,k-1):第c个骰子投了点数1
(c-1,k-2):第c个骰子投了点数2
(c-1,k-3):第c个骰子投了点数3
....
(c-1,k-6):第c个骰子投了点数6

在c-1个骰子的基础上,再增加一个骰子出现点数和为k的结果只有这6种情况!
所以:dp(c,k)=dp(c-1,k-1)+dp(c-1,k-2)+dp(c-1,k-3)+dp(c-1,k-4)+dp(c-1,k-5)+dp(c-1,k-6)(注意当k<6时的处理越界问题)
3.有1个骰子,dp(1,1)=dp(1,2)=dp(1,3)=dp(1,4)=dp(1,5)=dp(1,6)=1。

因此状态转移方程为

dp[c][k]=sum(dp[c-1][k-m])(1<=m<=6&&m

程序实现

public class Solution {
    public String[] printProbability(int n) {
        if (n <= 0)
            return null;
        //结果可能性总数
        int total = (int) Math.pow(6, n);
        String[] result = new String[6 * n - n + 1];

        //dp[c][k] c个骰子朝上一面点数之和为k的次数
        int[][] dp = new int[n + 1][6 * n + 1];
        //初始化dp[1][1...6]
        for (int x = 1; x <= 6; x++)
            dp[1][x] = 1;
        //执行计算
        for (int i = 2; i <= n; i++)
            for (int j = 2; j <= 6 * n; j++) {
                int sum = 0;
                for (int m = 1; m < j && m <= 6; m++)
                    sum += dp[i - 1][j - m];
                dp[i][j] = sum;
            }
        //统计结果,用分数表示
        for (int k = n; k <= 6 * n; k++) {
            result[k - n] = dp[n][k] + "/" + total;
        }

        return result;
    }

    public static void main(String[] args) {
        String[] result = new Solution().printProbability(3);
        System.out.println(result.length);
        for (String val : result)
            System.out.print(val + ",");
    }
}

计算结果

16
1/216,3/216,6/216,10/216,15/216,21/216,25/216,27/216,
27/216,25/216,21/216,15/216,10/216,6/216,3/216,1/216,

你可能感兴趣的:(算法,算法系列)