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 个元素里。

解法二:

  我们可以考虑用两个数组来存储骰子点数的每一个总数出现的次数。在一次循环中, 第一个数组中的第 n 个数字表示骰子和为 n 出现的次数。在下一循环中,我们加上一个新的骰子,此时和为 n 的骰子出现的次数应该等于上一次循环中骰子点数和为 n-1 、n-2 、n-3 、n-4, n-5 与 n-6 的次数的总和,所以我们把另一个数组的第 n 个数字设为前一个数组对应的第 n-1 、n-2 、n-3 、n-4、n-5 与 n-6 之和。

#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;
}

猜你喜欢

转载自www.cnblogs.com/tianzeng/p/10308858.html