【LeetCode】剑指 Offer 60. n个骰子的点数 p294 -- Java Version

news2024/11/20 11:27:59

题目链接:https://leetcode.cn/problems/nge-tou-zi-de-dian-shu-lcof/

1. 题目介绍(60. n个骰子的点数)

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

你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。

【测试用例】:

  • 掷骰子模拟器:还蛮有意思的。
    在这里插入图片描述

示例 1:

输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

示例 2:

输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

【条件约束】:

限制

  • 1 <= n <= 11

【相关题目】

题目1:计算n次掷有k个面的骰子实现给定和的方法总数 – Techie Delight
……
例如:
……
Input:

  • The total number of throws n is 2
  • TThe total number of faces k is 6 (i.e., each dice has values from 1 to 6)
  • TThe desired sum is 10

……
Output:

  • The total number of ways is 3.
  • The possible throws are (6, 4), (4, 6), (5, 5)

2. 前置知识

在掷骰子中探索组合的奥秘 – Komorebi

2.1 K - 骰子组合

给定骰子数,得到所有可能的组合
……
示例1:
输入:K = 2
输出:[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (2, 1 ), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4 , 6), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (6, 1), (6, 2 ), (6, 3), (6, 4), (6, 5), (6, 6)]
解释:打印所有可能的组合。
……
示例2:
输入:K = 1
输出:[(1, ), (2, ), (3, ), (4, ), (5, ), (6, )]
解释:打印所有可能的组合。

因为该题要求我们求出所有骰子🎲点数和的概率,那么我们首先要知道的就是 K个骰子一共有多少种组合的可能(允许重复),因为正常的骰子是 六面体,具有六个面,因此我们就可以得到公式 6k 来求出骰子组合的所有可能。

下面我们可以通过程序来验证一下公式是否正确:(以6个骰子为例)

2.1.1 Python版本

代码参考:Python – K Dice Combinations

# Python3 code to demonstrate working of
# K Dice Combinations
# Using list comprehension + product()
from itertools import product
# initializing K
K = input("请输入骰子个数:")
# using list comprehension to formulate elements
temp = [list(range(1, 7)) for _ in range(int(K))]
# using product() to get Combinations
res = list(product(*temp))
# printing result
print("The constructed dice Combinations : " + str(res))

我们可以看到,当有 6 个骰子时,其组合的所有可能为 46656,与我们通过公式使用计算器得来的结果一致:
在这里插入图片描述
在这里插入图片描述

2.1.2 Java版本

该方法基于 回溯 模板实现,通过不断向当前组合中添加数字,并在到达目标长度时将其添加到结果集中,最后返回所有可能的组合。

 // 返回 K 个骰子的所有可能的组合
    public static List<List<Integer>> getCombinations(int n) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> cur = new ArrayList<>();
        backtrack(n, cur, res);
        // 回溯结束,返回所有可能的组合
        return res;
    }
    private static void backtrack(int n, List<Integer> cur, List<List<Integer>> res) {
        // 到达目标长度时将其添加到结果集中
        if (cur.size() == n) {
            res.add(new ArrayList<>(cur));
            return;
        }
        // 遍历所有可能
        for (int i = 1; i <= 6; i++) {
            cur.add(i);
            backtrack(n, cur, res);
            // 移除 cur 列表中的最后一个元素
            cur.remove(cur.size() - 1);
        }
    }

运行结果:

在这里插入图片描述

3. 题解

3.1 动态规划(二维数组) – O(n2)

时间复杂度 O(n2),空间复杂度 O(n2)

解题思路】:
骰子一共有 6 个面,每个面上都有一个点数,对应的是 1~6 之间的一个数字。所以 n 个骰子的点数和的最小值为 n,最大值为 6n,即点数和范围为 [n,6n] 。另外根据排列组合的知识,我们还知道 n 个骰子的所有点数的排列数为 6n 。要解决这个问题,我们需要先统计出每个点数出现的次数,然后把每个点数出现的次数除以 6n,就能求出每个点数出现的概率。
……
依据上面的思想,我们可以使用 动态规划 来进行求解:
……
我们假设 f(n,s) 为 当骰子数为n,点数和为 s 时情况的出现的总数量,那么 当 n=1 时,就有 f(1,s) = 1,其中 s = 1,2,3,4,5,6;当 n ≥ 2 时,f(n,s) = f(n−1,s−1) + f(n−1,s−2) + f(n−1,s−3) + f(n−1,s−4) + f(n−1,s−5) + f(n−1,s−6),转化为公式,即:
在这里插入图片描述
……
实现策略】:
根据前面推理得到的公式,我们就可以进行以下编码:

  1. 定义二维数组 dp,用来记录 f(n,s) 的值,而由于下方for循环会取到 n和 6n,为了防止数组越界,所以数组的长度必须+1,同时空出 0 位不用;以 n = 6 为例:(Note:这里 [6n+1] 确实造成造成了不小的空间浪费,因为最后一层的实际范围位 [5*n+1],而其他层则是更小)
    在这里插入图片描述
  2. 开始根据公式进行 动态规划,当 n = 1时,怎么怎么样,当 n >= 2 时,怎么怎么样;
  3. 动规结束后,我们就可以求出 骰子所有点数的排列数 total,让 dp 数组中的最后一层 依次除以 总排列数,并将得到的结果存入 ans 后返回。
class Solution {
    // Solution2:二维数组:动态规划 
    public double[] dicesProbability(int n) {
        // 下方for循环会取到 n 和 6*n, 所以这里的数组长度必须+1
        int [][]dp = new int[n+1][6*n+1];
        //边界条件:
        // 当n = 1时,F(1,s)=1,其中s=1,2,3,4,5,6
        for(int s = 1; s <= 6; s++) dp[1][s] = 1;
        // 当n ≥ 2时,F(n,s)=F(n−1,s−1) + F(n−1,s−2) + F(n−1,s−3) + F(n−1,s−4) + F(n−1,s−5) + F(n−1,s−6)
        for(int i = 2; i<=n; i++){
            for(int s = i; s <= 6*i; s++){
                //求dp[i][s],即 f(n,s)
                for(int d = 1; d <= 6; d++){
                    if(s-d < i-1) break;//为0了 
                    dp[i][s] += dp[i-1][s-d];
                }
            }
        }
        // total表示总的排列数
        double total = Math.pow((double)6,(double)n);
        // ans表示s的所有取值:[n,6n]
        double[] ans = new double[5*n+1];
		// 开始计算概率
        for(int i = n; i <= 6*n; i++){
            ans[i-n]=((double)dp[n][i])/total;
        }
        // 计算完成,返回结果
        return ans;
    }
}

在这里插入图片描述

3.2 动态规划改进(一维数组) – O(n2)

在这里插入图片描述

状态转移公式:(与3.1 略有不同)
假设已知 n−1 个骰子的解 f(n−1) ,此时添加一枚骰子,求 n 个骰子的点数和为 x 的概率 f(n,x)
在这里插入图片描述
……
该题的通常做法是声明一个二维数组 dp,dp[i][j] 代表前 i 个骰子的点数和 j 的概率,并执行状态转移。而由于 dp[i] 仅由 dp[i−1] 递推得出,为降低空间复杂度,只建立两个一维数组 dp , tmp 交替前进即可。

class Solution {
    // Solution2:动态规划
    // 递推公式:f(n,s) = f(n-1.s-1)/6 + f(n-1.s-2)/6 + …… + f(n-1.s-6)/6
    public double[] dicesProbability(int n) {
        //因为最后的结果只与前一个动态转移数组有关,所以这里只需要设置一个一维的动态转移数组
        //原本dp[i][j]表示的是前i个骰子的点数之和为j的概率,现在只需要最后的状态的数组,所以就只用一个一维数组dp[j]表示n个骰子下每个结果的概率。
        //初始是1个骰子情况下的点数之和情况,就只有6个结果,所以用dp的初始化的size是6个
        double[] dp = new double[6];
        //只有一个数组
        Arrays.fill(dp,1.0/6.0);
        //从第2个骰子开始,这里n表示n个骰子,先从第二个的情况算起,然后再逐步求3个、4个···n个的情况
        //i表示当总共i个骰子时的结果
        for(int i=2;i<=n;i++){
        //每次的点数之和范围会有点变化,点数之和的值最大是i*6,最小是i*1,i之前的结果值是不会出现的;
        //比如i=3个骰子时,最小就是3了,不可能是2和1,所以点数之和的值的个数是6*i-(i-1),化简:5*i+1
            //当有i个骰子时的点数之和的值数组先假定是temp
            double[] temp = new double[5*i+1];
            //从i-1个骰子的点数之和的值数组入手,计算i个骰子的点数之和数组的值
            //先拿i-1个骰子的点数之和数组的第j个值,它所影响的是i个骰子时的temp[j+k]的值
            for(int j=0;j<dp.length;j++){
            //比如只有1个骰子时,dp[1]是代表当骰子点数之和为2时的概率,它会对当有2个骰子时的点数之和为3、4、5、6、7、8产生影响,因为当有一个骰子的值为2时,另一个骰子的值可以为1~6,产生的点数之和相应的就是3~8;比如dp[2]代表点数之和为3,它会对有2个骰子时的点数之和为4、5、6、7、8、9产生影响;所以k在这里就是对应着第i个骰子出现时可能出现六种情况,这里可能画一个K神那样的动态规划逆推的图就好理解很多
                for(int k=0;k<6;k++){
                    //这里记得是加上dp数组值与1/6的乘积,1/6是第i个骰子投出某个值的概率
                    temp[j+k]+=dp[j]*(1.0/6.0);
                }
            }
            //i个骰子的点数之和全都算出来后,要将temp数组移交给dp数组,dp数组就会代表i个骰子时的可能出现的点数之和的概率;用于计算i+1个骰子时的点数之和的概率
            dp = temp;
        }
        return dp;
    }   
}

在这里插入图片描述

4. 参考资料

[1] 动态规划 – shy
[2] 剑指 Offer 60. n 个骰子的点数(动态规划,清晰图解)-- Krahets

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/444408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue.js条件渲染指令v-if及v-show

目录 一、v-if 二、v-show 三、v-if与v-show的选择 一、v-if v-if是Vue.js的条件语句&#xff0c;v-if指令用于条件性地渲染一块内容&#xff0c;这块内容只会在指令的表达式返回true的时候被渲染。需要特别注意的是&#xff0c;v-if所关联的是Vue.js的动态变量。 v-if的使用…

如何计算连续变量的熵

背景 做特征选择时&#xff0c;有时候会用到计算特征的信息熵&#xff0c;可是离散的好计算&#xff0c;但连续的呢&#xff1f;按照把连续变量离散的方法设置阈值点吗&#xff1f;好像比较麻烦&#xff0c;需要排序&#xff0c; 计算阈值。没有能自动的方法吗&#xff1f; 找…

动物养殖虚拟仿真之生猪屠宰VR教学系统

生猪屠宰是一个复杂而危险的工作&#xff0c;需要有严格的操作规程和丰富的经验。但是传统的生猪屠宰培训存在一些问题&#xff0c;例如成本高、难以模拟真实场景等。 为了解决这些问题&#xff0c;VR技术被应用到生猪屠宰培训中&#xff0c;广州华锐互动由此开发了生猪屠宰VR…

BOM(1)

BOM&#xff1a;浏览器对象模型&#xff0c;它提供了独立于内容而与浏览器窗口进行交换的对象&#xff0c;其核心对象是window&#xff0c;由一系列相关对象构成&#xff0c;并且每个对象都提供了很多方法和属性BOM的构成&#xff1a;BOM比DOM更大&#xff0c;它包含DOM。 …

Docker中安装redmine(亲自安装有效)

第一步&#xff1a; 官方的一键安装方式&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun (我用的它) 国内 daocloud一键安装命令&#xff1a; curl -sSL https://get.daocloud.io/docker | sh ############################可能遇见问题##…

机器学习 day07(特征缩放)

特征缩放的作用 特征缩放可以让梯度下降算法运行的更快特征&#xff1a;X    对应的参数&#xff1a;W当一个特征的可能值范围很大时&#xff0c;一个好的模型会选择一个相对较小的对应参数值&#xff0c;因为W₁的一个非常小的变化会给估计价格产生非常大的影响&#xff0…

View系列

掌握View核心知识体系&#xff0c;两大方向&#xff1a;View事件分发&#xff0c;自定义View。 文章目录 一&#xff0c;基础知识1.1 页面创建1.2 页面管理 二&#xff0c;View事件分发2.1 基本概念2.2 分发流程2.3 面试题 三&#xff0c;View绘制3.1 measure&#xff08;测量&…

C#+asp.net基于web的大学社团管理信息系统

本系统的模块是分为用户模块和管理员模块&#xff0c;管理员负责学生管理模块、社团管理模块、公告管理模块、留言管理模块、加入社团管理模块、活动管理模块、管理员管理模块。社团管理员则负责预约管理模块、活动报名的审核等。。 系统具有三类系统用户分别是&#xff1a;系统…

linux网络

查看网络接口信息 ifconfig mtu 最大传输单元 mtu和mss区别 hostname命令 永久修改 hostnamectl set-hostname 切换shell环境生效 或者vi hostname 编辑完重启生效 查看路由表条目route route查看或设置主机中路由表信息 route -n将路由记录中的地址信息显示为数字形式 …

MTU 网卡bond 简介

MTU 最大传输单元MTU&#xff08;Maximum Transmission Unit&#xff0c;MTU&#xff09;&#xff0c;是指网络能够传输的最大数据包大小&#xff0c;以字节为单位。MTU的大小决定了发送端一次能够发送报文的最大字节数。如果MTU超过了接收端所能够承受的最大值&#xff0c;或者…

App Inventor 2 开发问答App

应用介绍 一个最基本的问答App开发&#xff0c;问答数据源来自csv文件格式&#xff0c;方便后续拓展成网络版的问答App。 事先出好题目、ABCD选择项及正确答案&#xff0c;先存在列表中&#xff0c;然后按顺序出题&#xff0c;答对则继续下一题&#xff0c;答错则Game over。 …

人工智能的前沿信息获取之使用谷歌学术搜索

谷歌学术是谷歌公司开发的一款专门针对学术搜索的在线搜索引擎[4]&#xff0c;谷歌学术的网址为https://scholar.google.com&#xff0c;界面如图 6‑1所示。使用谷歌学术搜索可以检索会议或者期刊论文。只需要在检索框中输入关键字&#xff0c;然后点搜索按钮即可&#xff0c;…

了解Transformer架构的前奏_什么是预训练_理解预训练---人工智能工作笔记0034

我们会先来说预训练有什么用,其实 之前说的机器学习,其实都是跟数学相关性很大的,比如,支持向量机,回归算法, 1.最早的时候,做机器学习,就是偏数学的,比如用的决策树,支持向量机,线性回归,逻辑回归等算法. 这种是偏向数学的,偏向统计的. 然后这个深度学习,其实就是偏大数据的…

奥艺大会 | “OLYMP‘ARTS中国设计奖”在2023米兰设计周发布

由国际奥艺委员会、北京国际设计周和中国科学院大学魏桥国科联合实验室共同发起的“OLYMPARTS中国设计奖”&#xff08;OlympArts China Design Awards&#xff09;于当地时间2023年4月19日&#xff0c;在2023米兰设计周“中国日”活动中举行宣传推介活动。 &#xff08;由左至…

缺失msvcp140.dll怎么办?msvcp140.dll下载

缺失msvcp140.dll怎么办&#xff1f;msvcp140.dll下载&#xff0c;作为Windows操作系统中必备的组件之一&#xff0c;msvcp140.dll是一款Microsoft Visual C Runtime的动态链接库文件&#xff0c;旨在提供必要的C运行环境支持&#xff0c;以让软件应用程序得以在Windows平台上可…

道可维斯|益企行动,点亮星空数字化转型峰会

2023年&#xff0c;“烟火气”回归&#xff0c;但企业挑战仍在继续。找寻企业增长转型的内生动力&#xff0c;仍是中小企业不变的探索话题。如何寻找穿越周期的高成长机会&#xff1f;4月21日&#xff0c;佛山金蝶软件科技有限公司主办的主题为“益企行动&#xff0c;点亮星空”…

MFC转QT踩坑记录

1、中文乱码 QT msvc编译器版本默认编译的是字符串编码是ANSI&#xff0c; 而QTCreator默认创建的cpp字符串编码是UTF-8&#xff0c;然后msvc还是按ANSI去解析字符串常量&#xff0c;所以导致了中文乱码 解决方案&#xff1a; 使用notepad把cpp编码从UTF-8转成 UTF-8带BOM…

ChatGPT 之父承认 GPT-5 并不存在,为什么 OpenAI 总是这么实诚?|万字详述

ChatGPT 诞生前传 来源: 爱范儿 微信号&#xff1a;ifanr 最近&#xff0c;OpenAI 的 CEO Sam Altman 在一场公开会议上为 GPT-5 辟谣。 他声称 OpenAI 并没有在训练 GPT-5&#xff0c;而是一直基于 GPT-4 做别的工作。 OpenAI 是一家非常有趣的机构&#xff0c;和微软、Go…

用SQL语句操作Oracle数据库——数据更新

数据更新 数据库中的数据更新操作有3种&#xff1a;1)向表中添加若干行数据&#xff08;增&#xff09;&#xff1b;2&#xff09;删除表中的若干行数据&#xff08;删&#xff09;&#xff1b;3&#xff09;修改表中的数据&#xff08;改&#xff09;。对于这3种操作&#xf…

提升项目沟通效果的核心方法

项目沟通是项目管理中的核心之一&#xff0c;项目成败的关键因素之一就是项目团队之间的沟通效果。良好的项目沟通可以增强团队的合作力和凝聚力&#xff0c;确保项目按时完成&#xff0c;达成项目目标。那么提升项目沟通效果的方法有哪些呢&#xff1f;。1、制定沟通计划 在项…