我的个人微信公众号:Microstrong
微信公众号ID:MicrostrongAI
微信公众号介绍:Microstrong(小强)同学主要研究机器学习、深度学习、计算机视觉、智能对话系统相关内容,分享在学习过程中的读书笔记!期待您的关注,欢迎一起学习交流进步!
知乎主页:https://www.zhihu.com/people/MicrostrongAI/activities
题目链接
https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof/
题目描述
解题思路
(1)递归解法
import math
from typing import List
class Solution:
# 定义骰子最大点数
g_maxValue = 6
# 方法一:基于递归求骰子点数,时间效率不够高
def twoSum(self, n: int) -> List[float]:
if n < 1:
return []
# 定义n个骰子的最大点数
maxSum = n * self.g_maxValue
# 所有可能的值出现的次数保存在列表中
pProbabilities = [0] * (maxSum - n + 1)
# 递归中的第一个骰子有6种情况
for i in range(1, self.g_maxValue + 1):
# 递归剩余的(n-1)个骰子
self.getProbability(n, n, i, pProbabilities)
# n个骰子的所有点数的排列数为6的n次方
total = math.pow(self.g_maxValue, n)
res = list(map(lambda x: x / total, pProbabilities))
return list(map(lambda x: round(x, 5), res))
def getProbability(self, original, current, sum, pProbabilities):
'''
:param original: 表示n个骰子仍在地上的n
:param current: 剩余需要递归的骰子数
:param sum: 骰子点数相加的数
:param pProbabilities:
:return:
'''
# 递归结束条件
if current == 1:
pProbabilities[sum - original] += 1
else:
for i in range(1, self.g_maxValue + 1):
self.getProbability(original, current - 1, i + sum, pProbabilities)
(2)动态规划解法
使用动态规划解决问题一般分为三步:
- 表示状态
- 找出状态转移方程
- 边界处理
下面我们一步一步分析,相信你一定会有所收获!
1. 表示状态
分析问题的状态时,不要分析整体,只分析最后一个阶段即可!因为动态规划问题都是划分为多个阶段的,各个阶段的状态表示都是一样,而我们的最终答案就是在最后一个阶段。
对于这道题,最后一个阶段是什么呢?
通过题目我们知道一共投掷 n 枚骰子,那最后一个阶段很显然就是:当投掷完 n 枚骰子后,各个点数出现的次数。
注意,这里的点数指的是前 n 枚骰子的点数和,而不是第 n 枚骰子的点数,下文同理。
找出了最后一个阶段,那状态表示就简单了。
- 首先用数组的第一维来表示阶段,也就是投掷完了几枚骰子。
- 然后用第二维来表示投掷完这些骰子后,可能出现的点数。
- 数组的值就表示,该阶段各个点数出现的次数。
所以状态表示就是这样的:,表示投掷完 i 枚骰子后,点数 j 的出现次数。
2. 找出状态转移方程
找状态转移方程也就是找各个阶段之间的转化关系,同样我们还是只需分析最后一个阶段,分析它的状态是如何得到的。
最后一个阶段也就是投掷完 n 枚骰子后的这个阶段,我们用 来表示最后一个阶段点数 j 出现的次数。
单单看第 n 枚骰子,它的点数可能为 ,因此投掷完 n 枚骰子后点数 j 出现的次数,可以由投掷完 枚骰子后,对应点数 出现的次数之和转化过来。
for (第n枚骰子的点数 i = 1; i <= 6; i ++) {
dp[n][j] += dp[n-1][j - i]
}
写成数学公式是这样的:
n 表示阶段,j 表示投掷完 n 枚骰子后的点数和,i 表示第 n 枚骰子会出现的六个点数。
3. 边界处理
这里的边界处理很简单,只要我们把可以直接知道的状态初始化就好了。
我们可以直接知道的状态是啥,就是第一阶段的状态:投掷完 1 枚骰子后,它的可能点数分别为 ,并且每个点数出现的次数都是 1 。
for (int i = 1; i <= 6; i ++) {
dp[1][i] = 1;
}
代码:
# 动态规划解法
def twoSum2(self, n: int) -> List[float]:
# 初始化二维数组
dp = [[0 for _ in range(6 * n + 1)] for _ in range(n + 1)]
# 初始化第一行
for i in range(1, 7):
dp[1][i] = 1
for i in range(2, n + 1):
for j in range(i, i * 6 + 1):
for k in range(1, 7):
if j >= k + 1:
dp[i][j] += dp[i - 1][j - k]
res = []
for i in range(n, n * 6 + 1):
res.append(dp[n][i] * 1.0 / 6 ** n)
return list(map(lambda x: round(x, 5), res))
Reference
【1】《剑指offer》,何海涛著。
【2】Leetcode n个骰子的点数---动态规划,地址:https://blog.csdn.net/qq_24243877/article/details/104560944
【3】【n个骰子的点数】:详解动态规划及其优化方式,地址:https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof/solution/nge-tou-zi-de-dian-shu-dong-tai-gui-hua-ji-qi-yo-3/
来源:oschina
链接:https://my.oschina.net/u/4256877/blog/4306209