n 个骰子的点数

题目

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

思路

解法一:

  先把 n 个骰子分为两堆:第一堆只有一个,另一个有 n-1 个。单独的那一个有可能出现从 1 到 6 的点数。我们需要计算从 1 到 6 的每一种点数和剩下的 n-1 个骰子来计算点数和。接下来把剩下的 n-1 个骰子还是分成两堆,第一堆只有一个, 第二堆有 n-2 个。我们把上一轮那个单独骰子的点数和这一轮单独骰子的点数相加, 再和剩下的 n-2 个骰子来计算点数和。分析到这里,我们不难发现这是一种递归的思路,递归结束的条件就是最后只剩下一个骰子。

我们可以定义一个长度为“6n-n+1”的数组, 和为 s 的点数出现的次数保存到数组第 s-n 个元素里。

解法二:

  1.现在变量有:骰子个数,点数和。当有c个骰子,点数和为k时,出现次数记为dp(c,k)。
  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<k)
#include <iostream>
#include <cmath>
using namespace std;

const int max_value=6;//单个骰子的最大值 

class Solution
{
    public:
        void probality(int n);//递归 
        void probality(int original,int index,int curr_sum,int *value);
        void cricile_probality(int n);//基于循环解法 
};
void Solution::probality(int n)
{
    if(n<1)
        return;
    
    int max_sum=n*max_value;//n个骰子的最大值 
    int *value=new int[max_sum-n+1];//定义6*n-n+1的数组
    for(int i=n;i<=max_sum;++i)
        value[i-n]=0;//将和为s的点数存放到数组的第s-n个元素里
    
    int curr_sum=0;
    probality(n,n,curr_sum,value); 
    
    int sum=pow((double)max_value,n);
    for(int i=n;i<=max_sum;++i)
    {
        double ratio=(double)value[i-n]/sum;
        cout<<i<<' '<<ratio<<endl;
    }
    
    delete []value;
    return;
}
void Solution::probality(int original,int index,int curr_sum,int *value)
{
    if(index==0)
    {
        value[curr_sum-original]+=1;
        return;
    }
    for(int i=1;i<=max_value;++i)
        probality(original,index-1,i+curr_sum,value);
}
void Solution::cricile_probality(int n)
{
    if(n<1)
        return;
    int *value[2];
    value[0]=new int[max_value*n+1]; 
    value[1]=new int[max_value*n+1];
    for(int i=0;i<max_value*n+1;++i)
    {
        value[0][i]=0;
        value[1][i]=1;
    }
    
    int flag=0;//标记当前使用的是0数组还是1数组
    for(int i=1;i<=max_value;++i)//第一次投骰子 
        value[flag][i]=1; 
    
    //抛出其他骰子
    for(int k=2;k<=n;++k)
    {
        for(int i=0;i<k;++i)//如果抛出了k个骰子,那么和为[0, k-1]的出现次数为0
            value[1-flag][i]=0;
        
        for(int i=k;i<=max_value*k;++i)//抛出k个骰子,所有和的可能
        {
            value[1-flag][i]=0;
            for(int j=1;j<=i&&j<=max_value;++j)
                value[1-flag][i]+=value[flag][i-j];
        }
        flag=1-flag;
    }
    int sum=pow((double)max_value,n);
    for(int i=n;i<=max_value*n;++i)
    {
        double ratio=(double)value[flag][i]/sum;
        cout<<i<<' '<<ratio<<endl;
    }
    
    for(int i=0;i<max_value*n+1;++i)
    {
        delete []value[0];
        delete []value[1];
    }
    return;
}
int main()
{
    Solution s;
    s.probality(6);
    s.cricile_probality(6);
    return 0;
}
原文地址:https://www.cnblogs.com/tianzeng/p/10308858.html