剑指Offer对答如流系列 - n个骰子的点数

面试题60:n个骰子的点数

题目描述

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

问题分析

这个问题需要点高中数学的知识。

对于n个骰子,要计算出每种点数和的概率,我们知道投掷n个骰子的总情况一共有6^n种,因此只需要计算出某点数和的情况一共有几种,即可求出该点数之和的概率。

思路一:递归暴力

我们知道点数之和s的最小值为n,最大值为6*n,因此考虑用一个大小为(6*n-n+1)的数组存放不同点数之和的情况个数,那么,如果点数之和为x,那么把它出现的情况总次数放入数组种下标为x-n的元素里。

确定了如何存放不同点数之和的次数之后,我们要计算出这些次数。我们把n个骰子分为1个骰子和n-1个骰子,这1个骰子可能出现1~6个点数,由该骰子的点数与后面n-1个骰子的点数可以计算出总点数;而后面的n-1个骰子又可以分为1个和n-2个,把上次的点数,与现在这个骰子的点数相加,再和剩下的n-2个骰子的点数相加可以得到总点数……,即可以用递归实现。在获得最后一个骰子的点数后可以计算出几个骰子的总点数,令数组中该总点数的情况次数+1,即可结束遍历。

可以感受到,计算量实在太大了,效率会比较低。

思路二:动态规划

剑指Offer的解答过于繁琐与复杂。这里提供一种相对理解简单,容易实现的方法。

我们通过分析能够发现 n个骰子的点数依赖于n-1个骰子的点数,相当于在n-1个骰子点数的基础上再进行投掷。

由此定义状态转移方程为f(n,k)表示n个骰子点数和为k时出现的次数,于是可得:

f(n,k)=f(n?1,k?1)+f(n?1,k?2)+f(n?1,k?3)+f(n?1,k?4)+f(n?1,k?5)+f(n?1,k?6)

其中 n>0且k<=6n,f(n?1,k?i)表示的是第n次掷骰子时,骰子的点数为i对应的情况

从k?1到k?6分别对应第n次掷骰子时骰子正面为1到6的情况。而初始状态可以定义为:

f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1

问题解答

思路一:

    private final int maxValue = 6;

    public  void printProbability1(int number) {
        if(number<=0) {
            return;
        }

        int[] probabilities = new int[maxValue*number-number+1];
        Arrays.fill(probabilities,0);

        //计算不同点数出现的次数
        for(int i=1;i<=maxValue;i++) {
            //第一次掷骰子,总点数只能是1~maxValue(即6)
            calP(probabilities, number, number-1, i);
        }

        //所有情况总共出现的次数
        int totalP = (int) Math.pow(maxValue, number);
        for(int i=0; i<probabilities.length; i++) {
            double ratio = (double)probabilities[i]/totalP;
            NumberFormat format = NumberFormat.getPercentInstance();
            format.setMaximumFractionDigits(2);//设置保留几位小数
            System.out.println("点数和为"+(i+number)+"的概率为:"+format.format(ratio));
        }
    }

    private  void calP(int[] probabilities, int number, int curNumber, int sum) {
        if(curNumber==0) {
            probabilities[sum-number]++; //总数为sum的情况存放在sum-number下标中
            return;
        }
        for(int i=1; i<=maxValue; i++) {
            // 相当于剩余的骰子少一个,总点数增加。
            calP(probabilities, number, curNumber-1, sum+i);
        }
    }

思路二:

  public  void printProbability(int number) {
        double total = Math.pow(6,number);
        for(int i=number; i<=6*number; i++) {
            double ratio = (double)getNSumCount(number,i)/total;
            NumberFormat format = NumberFormat.getPercentInstance();
            format.setMaximumFractionDigits(2);//设置保留几位小数
            System.out.println("点数和为"+getNSumCount(number,i)+"的概率为:"+format.format(ratio));
        }
    }

    private int getNSumCount(int n, int sum) {
        if(n<1 || sum<n || sum>6*n) {
            return 0;
        }
        if(n==1) {
            return 1;
        }
        int resCount;
        resCount = getNSumCount(n-1,sum-1)+getNSumCount(n-1,sum-2)+
                getNSumCount(n-1,sum-3)+getNSumCount(n-1,sum-4)+
                getNSumCount(n-1,sum-5)+getNSumCount(n-1,sum-6);
        return resCount;
    }

原文地址:https://www.cnblogs.com/JefferyChenXiao/p/12249457.html

时间: 2024-08-29 03:04:21

剑指Offer对答如流系列 - n个骰子的点数的相关文章

剑指Offer对答如流系列 - 礼物的最大价值

面试题47:礼物的最大价值 题目描述 在一个m×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0).你可以从棋盘的左上角开始拿格子里的礼物,并每次向左或者向下移动一格直到到达棋盘的右下角.给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物? 比如下面的棋盘中,如果按照红色数字的路线走可以拿到最大价值为53的礼物 问题分析 动态规划:定义f(i,j)为到达(i,j)位置格子时能拿到的礼物总和的最大值,则有:f(i,j)=max{f(i-1,j),f(i,j-1)}+va

剑指Offer对答如流系列 - 序列化二叉树

面试题37:序列化二叉树 题目描述 请实现两个函数,分别用来序列化和反序列化二叉树. 树的结构定义如下: public class Node { int val = 0; Node left = null; Node right = null; public Node(int val) { this.val = val; } } 问题分析 一般情况下,需要采用前/后序遍历和中序遍历才能确定一个二叉树,具体的内容我们之前探讨过 剑指Offer对答如流系列 - 重建二叉树 但是采用这种方式进行序列化

剑指Offer对答如流系列 - 把数组排成最小的数

面试题45:把数组排成最小的数 题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3, 32, 321},则打印出这3个数字能排成的最小数字321323. 问题分析 之前我们做过字符全排列的习题 剑指Offer对答如流系列 - 字符串的排列,但是将算法思想应用到这一题的话,效果不好,求出所有的组合,再计算出组合的最小值,这效率该多低啊. 我们还要进一步探究,看看有没有不错的规律,供我们使用. 因为数字拼接后的长度一样,拼接后的结果

剑指Offer对答如流系列 - 从上往下打印二叉树

面试题32:从上往下打印二叉树 题目描述 树的结构定义如下: public class Node{ int e; Node left; Node right; Node(int x) { e = x; } } (一)不分行从上到下打印二叉树 从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印. 比如下面二叉树,输出顺序为 8 6 10 5 7 9 11 (二)分行从上到下打印二叉树 从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行. 比如下面二叉树,输出

剑指Offer对答如流系列 - 数组中数字出现的次数

面试题56:数组中数字出现的次数 题目描述 问题(1)数组中只出现一次的两个数字 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 问题(2)数组中唯一只出现一次的数字 在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次.请找出那个只出现一次的数字. 问题分析 问题(1)分析 在这篇文章剑指Offer对答如流系列 - 二进制中 1 的个数中,我们详细探讨了位运算,其中有重要的一条:两个相同的数异

剑指Offer对答如流系列 - 求1+2+…+n

面试题64:求1+2+-+n 题目描述 求1+2+-+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 问题分析 有了那么多限制,剩下的我们可以选择 单目运算符:++和--,双目运算符:+,-,移位运算符<>,关系运算符>,<等 逻辑运算符&&,||,&,|,^,赋值= 既然是一个等差数列,和为(n+1)*n/2 我们之前详细探讨了位运算剑指Offer对答如流系列 - 二进制中 1 的个

剑指Offer对答如流系列 - 不用加减乘除做加法

面试题65:不用加减乘除做加法 题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.×.÷四则运算符号. 问题分析 我们之前详细探讨了位运算 剑指Offer对答如流系列 - 二进制中 1 的个数,已经非常非常详细了. 这道题仅仅是让做加法,我们除此之外还是做了乘除与减法. 记不清的朋友可以回头看看. 这里象征性地做一次解答吧 问题解答 public int add(int num1,int num2) { while(num2!=0){ int sum=num1^num2; in

剑指Offer对答如流系列 - 从1到n整数中1出现的次数

面试题43:从1到n整数中1出现的次数 题目描述 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数. 例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次. 问题分析 最容易想到的思路是通过对10求余数判断整数的个位数字是不是1.代码书写也很简单,但是如果输入的整数n比较大的时候,会有大量的运算. 1是由于数字递增出现的,而十进制影响这种出现的周期性.这本身肯定存在规律,重要的是耐心寻找,不要妄想一次性就找出来,下面的规律要比<剑指Offer>

剑指Offer对答如流系列 - 二进制中 1 的个数

面试题14:二进制中 1 的个数 题目描述 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数.例如把9表示成二进制是1001,有2位是1.因此如果输入9,该函数输出2. 问题分析与解决 这道面试题归属于 <剑指Offer>位运算章节.遇到二进制相关的问题,很容易想到位运算,虽然种类不多(与.或.异或.左移.右移),但是搞起来是千变万化的.待会再和你侃一些骚操作,我们先看这道题. (一)思路一 "与运算"有一个性质:通过与对应位上为1,其余位为0的数进行与运算,可以