【面试手撕】拼多多一面:n个骰子的点数

190 阅读2分钟

题目描述

leetcode.cn/problems/ng…

你选择掷出 num 个色子,请返回所有点数总和的概率。

你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 num 个骰子所能掷出的点数集合中第 i 小的那个的概率。

解题思路

参考K神的解题思路:leetcode.cn/problems/ng…

动态规划DP,建立dp数组,dp[i][j] 表示前i个筛子,点数和为 j 的概率。

如何求dp[i][j]?

dp[i][j]的计算建立在dp[i-1][x] 的基础上,举个例子,现在知道第一个筛子,点数和为1,2,3,4,5,6的概率分别为 1/6,1/6,1/6,1/6,1/6,1/6

如何求dp[2][5], 求前两个筛子点数和为5的概率。

dp[2][5] = dp[1][5-1]*1/6 + dp[1][5-2]*1/6 + dp[1][5-3]*1/6 + dp[1][5-4]*1/6 的概率和

也就是(第一个点数为4,第二个点数为1)(第一个点数为3,第二个点数为2)(第一个点数为2,第二个点数为3)(第一个点数为1,第二个点数为4)这些情况的概率和

image.png

所以动态转移方程为

dp[i][j] = dp[i-1][j-1]*1/6  + dp[i-1][j-2]*1/6 + dp[i-1][j-3]*1/6 + dp[i-1][j-4]*1/6 + dp[i-1][j-5]*1/6 + dp[i-1][j-6]*1/6

前提是j要大于等于骰子的点数

代码

可以自己在纸上画一个二维DP数组图看一看,就很清晰了

public class solution {
    //https://leetcode.cn/problems/nge-tou-zi-de-dian-shu-lcof/solutions/637778/jian-zhi-offer-60-n-ge-tou-zi-de-dian-sh-z36d/
    // dp
    // dp[i][j] 表示前i个筛子,点数和为j,的概率,类似于背包问题
    // dp[i][j] = dp[i-1][j-1]*1/6  + dp[i-1][j-2]*1/6 + dp[i-1][j-3]*1/6 + dp[i-1][j-4]*1/6 + dp[i-1][j-5]*1/6 + dp[i-1][j-6]*1/6
    // TODO 自己在纸上画一个dp数组就行了
    public double[] statisticsProbability(int num) {
        int height = num+1;
        int wid = num*6+1;

        double[][] dp = new double[height][wid];
        // 初始化第1行
        for (int i = 1; i <= 6; i++) {
            dp[1][i] = 1.0/6.0d;
        }
        for (int i = 2; i <= num; i++){
            for(int j = i; j <= i*6; j++){
                dp[i][j] = 0;
                // 置筛子的点数
                for(int x =1; x <= 6; x++){
                    if (x <= j){
                        dp[i][j] += dp[i-1][j-x]*1.0/6.0d;
                    }
                }
            }
        }
        int begin = num;
        int end = num*6;
        int index = 0;
        double[] res = new double[end - begin + 1];
        for(int i = begin; i <= end; i++){
            res[index++] = dp[num][i];
        }
        return res;
    }
}