• 【Offer】[60] 【n个骰子的点数】


    题目描述

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

    [牛客网刷题地址]无

    思路分析

    1. 递归的方法。要想求出n个骰子的点数和,可以先把n个骰子分为两堆:第一堆只有一个;另一堆有n-1个。单独的那一个有可能出现1~6的点数。我们需要计算1~6的每一种点数和剩下的n-1个骰子来计算点数和。接下来把剩下的n-1个骰子仍然分成两堆:第一堆只有一个;第二堆有n-2个。我们把上一轮那个单独骰子的点数和这一轮单独骰子的点数相加,再和剩下的n-2个骰子来计算点数和。分析到这里,我们不难发现这是一种递归的思路,递归结束的条件就是最后只剩下一个骰子。我们可以定义一个长度为6n n+1的数组,将和为s的点数出现的次数保存到数组的第s-n个元素里。
    2. 可以换一种思路来解决这个问题。我们可以考虑用两个数组来存储骰子点数的每个总数出现的次数。在一轮循环中,第一个数组中的第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个数字之和。

    测试用例

    1. 功能测试:1、2、3、4个骰子的各点数的概率。
    2. 特殊输入测试:输入0。
    3. 性能测试:输入较大的数字,如11。

    Java代码

    public class Offer060 {
        public static void main(String[] args) {
            test1();
            test2();
            test3();
            
            
    
        }
    
        private static final int maxValue = 6;
    
        public static void printProbability1(int number) {
            Solution1(number);
        }
    
        /**
         * 方法一:递归解法
         */
        private static void Solution1(int number) {
            if (number <= 0)
                return; // 错误
            int[] probabilities = new int[maxValue * number - number + 1];
            // 下标为i,对应的值代表点数之和为i+number总共出现的情况次数
            // 点数从number~maxValue*number,所以数组大小为6*number-number+1
            for (int i = 0; i < probabilities.length; i++)
                probabilities[i] = 0;
    
            // 计算不同点数出现的次数
            for (int i = 1; i <= maxValue; i++)
                calP(probabilities, number, number - 1, i); // 第一次掷骰子,总点数只能是1~maxValue(即6)
    
            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));
            }
        }
    
        /**
         * 计算每种点数出现的次数
         * 
         * @param number:骰子总个数
         * @param curNumber:当前剩余骰子个数
         * @param sum:各个骰子加起来的总点数
         */
        private static 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); // 相当于剩余的骰子少一个,总点数增加。
        }
    
    /**
         * 方法二
         * @param number
         */
        private static void Solution2(int number) {
            if (number <= 0)
                return; // 错误
            int[][] probabilities = new int[2][number * maxValue + 1];
            // [2]代表用两个数组交替保存,[number*maxValue+1]是指点数为所在下标时,该点数出现的总次数。
            // probabilities[*][0]是没用的,只是为了让下标对应点数
    //      for (int i = 0; i < 2; i++) {
    //          for (int j = 0; j < number * maxValue; j++) {
    //              probabilities[i][j] = 0;
    //          }
    //      }
    
            for (int i = 1; i <= 6; i++)
                probabilities[0][i] = 1; // 第一个骰子出现的情况
    
            int flag = 0;
            for (int curNumber = 2; curNumber <= number; curNumber++) { // 当前是第几个骰子
                for (int i = 0; i < curNumber; i++)
                    probabilities[1 - flag][i] = 0; // 前面的数据清零
    
                for (int i = curNumber; i <= curNumber * maxValue; i++) {
                    for (int j = 1; j <= 6 && j <= i; j++) {
                        probabilities[1 - flag][i] += probabilities[flag][i - j];
                    }
                }
                flag = 1 - flag;
    
            }
    
            int totalP = (int) Math.pow(maxValue, number); // 所有情况总共出现的次数
            for (int i = number; i <= number * 6; i++) {
                double ratio = (double) probabilities[flag][i] / totalP;
                NumberFormat format = NumberFormat.getPercentInstance();
                format.setMaximumFractionDigits(8);// 设置保留几位小数
                System.out.println("点数和为" + (i + number) + "的概率为:" + format.format(ratio));
            }
        }
    
        private static void test1() {
    
        }
    
        private static void test2() {
    
        }
    
        private static void test3() {
    
        }
    
    }
    

    代码链接

    剑指Offer代码-Java

  • 相关阅读:
    jichu
    scanf
    数位dp
    hdu 5667
    华东交通大学2018年ACM“双基”程序设计竞赛 K
    华东交通大学2018年ACM“双基”程序设计竞赛 D
    map
    次小生成树
    set
    c++11之为什么C++11引入了std::ref?
  • 原文地址:https://www.cnblogs.com/haoworld/p/offer60-n-ge-tou-zi-de-dian-shu.html
Copyright © 2020-2023  润新知