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

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

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

示例 1:

输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
示例 2:

输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

限制:

1 <= n <= 11

通过次数19,160提交次数35,573

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


思路:
动态规划,题目的意思比较绕,可以这样来理解。掷n个骰子,可能的点数是n~6*n,求的是每个点数出现的概率。而易得,n个骰子,总共有6^n种可能性,所以,我们要求的是每个点数出现的次数即可求得概率。考虑使用动态规划求解:
1. 确认状态。dp[i][j]表示投了i个骰子之后,点数为j出现的次数;
2. 状态转移方程。考虑投了n-1个骰子,第n个骰子有1-6个点数的可能性,那么对dp[n-1][j-1]~dp[n-1][j-6]求和,即为dp[n][j]的值;
3. 确认边界条件。即dp[1][1~6]=1;
更进一步的,可以观察到,对于每次状态转移,需要用到的只是前一个阶段的某些状态,因此可以将二维dp数组进行优化,使用一维数组,来进行更新。而为了更新的时候不会影响到需要保存的状态,在每一个阶段(投新的骰子),需要从后往前进行更新。(形象的理解在dp数组中,更新的图是向右下的一个不断扩大的类似阶梯的形状)
注:需要注意的是,再循环体的内部的判断,包括边界值。例如,如果当前是第i个骰子


//二维
class Solution {
public:
    vector<double> twoSum(int n) {
        int dp[12][73];
        memset(dp,0,sizeof(dp));

        for(int i = 1;i<=n;i++)
        {
            if(i==1)
            {
                for(int j = 1;j<7;j++)
                {
                    dp[i][j] = 1;
                }
            }
            else
            {
                for(int j = i;j<=6*i;j++)
                {
                    for(int k = 1;k<7;k++)
                    {
                        if(j-k<=0)break;
                        dp[i][j]+=dp[i-1][j-k];
                    }
                }
            }
        }

        vector<double> ret;
        int all = pow(6,n);
        for(int i = n;i<=6*n;i++)
        {
            ret.push_back((double)dp[n][i]/(double)all);
        }
        return ret;
    }
};

//一维
class Solution {
public:
    vector<double> twoSum(int n) {
        vector<int> dp(70,0);
        for(int i = 1;i<=6;i++)
        {
            dp[i]=1;
        }
        for(int i = 2;i<=n;i++)
        {
            for(int j = i*6;j>=i;j--)
            {
                dp[j]=0;
                for(int k =1;k<=6;k++)
                {
                    if(j-k<i-1)
                    {
                        break;
                    }
                    //cout<<j-k<<endl;
                    dp[j]+=dp[j-k];
                }
            }
        }
        vector<double> ret;
        int all = pow(6,n);
        for(int i = n;i<=6*n;i++)
        {
            ret.push_back((double)dp[i]/(double)all);
        }
        return ret;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值