n个骰子的点数

剑指offer上43题

n个骰子,朝上一面的点数之和为S,求S的所有可能的值的概率

有两种方法 1.递归

2.动态规划 f(n) = f(n-1)+f(n-2)+....+f(n-6);

贴代码:

package testAbstractClass;

public class xiecheng {
    //有n个骰子,所有朝上的点数之和为S 求S的所有可能值出现的概率。
    public static int six=6;
    public static void main(String args[]){
        //printpro(2);
        printProbability(2);
        
    }     
    //以下为递归解法
    public static void printProbability(int n){
        if(n<1) return;
        int maxsum = n*six;
        int minsum = n*1;
        //将和为s的出现次数放在s-n个位置 index=s-n;index一共是0-5n
        int [] mark_num = new int [maxsum-minsum+1];//自动初始化为0;
        probability(n, mark_num);
       
        int total = (int) Math.pow(six, n);//总共的可能数目为6^n
        for(int i=n;i<=maxsum;i++){
            double ratio = (double)mark_num[i-n]/total;
            System.out.println("和为"+i                    +"出现的概率是"+ratio);
        }
    }
    //获取所有的可能 并将出现的次数存在一个5n+1的数组中;
    public static void probability(int n,int [] mark_num){
        for(int i =1;i<=six;i++){
            probability(n,n,i,mark_num);
        }
    }
    //递归实现 将n个骰子,目前递归到序号,目前的和,以及标记数组传进来。当序号递归6次 就完成一次记录 ,将对应位置上的值加1
    public static void probability(int original,int current,int sum,int [] mark_sum){
        if(current==1) mark_sum[sum-original]++;
        else{
            for(int i=1;i<=six;i++){
                probability(original,current-1,i+sum,mark_sum);
            }
        }
        }
  
       }

 

第二种方法是动态规划,记录前一轮状态中的骰子的值 

/**
     * 解法2 动态规划法;用两个数组存储骰子点数的每一个总数出现的次数: 数组的第n个数字达标骰子和为n出现的次数,
     * 在下一轮循环中,加上一个新的骰子,此时和为n的骰子出现的次数应该等于上一轮骰子点数为n-1,n-2,n-3,n-4,n-5,n-6的次数总和。
     * f(n) = f(n-1)+...f(n-6);
     */
    public static  void printpro(int n){
        int [][] pro =  new int [2][six*n+1];
        
        
        int flag =0;
        //第一个骰子的状态 就是dp的出示状态
        for(int i=1;i<=six;i++) pro[flag][i] =1;
        //第二个骰子到第n个骰子
        for(int j=2;j<=n;j++){
            //将前0-j置零 因为j个骰子 点数最少都是j*1
            for(int i=0;i<j;i++) pro[1-flag][i] =0;
            
            for(int i=j;i<=six*j;i++){
                pro[1-flag][i] =0;
                //i-1 i-2...i-6
                for(int k=1;k<=i&&k<=six;k++){
                    pro[1-flag][i]+=pro[flag][i-k];
                }
            }
            //遍历完一次更改一次flag
            flag =1-flag;
            
        }
        int maxsum = n*six;
        double total = (int) Math.pow((double)six, (double)n);//总共的可能数目为6^n
        for(int i=n;i<=maxsum;i++){
            double ratio = (double)pro[flag][i]/total;
            System.out.println("和为"+i+"出现的概率是"+ratio);
        }
        
    }
        
        
        

第二种方法时间上效率较高。只比第一种方法多费了o(n)的空间。

posted @ 2017-03-28 19:07  王小丸子  阅读(242)  评论(0编辑  收藏  举报