剑指Offer 60.n个骰子的点数(Python)

题目描述

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


解题思路

其本质是求数列 f ( n ) = f ( n − 1 ) + f ( n − 2 ) + f ( n − 3 ) + f ( n − 4 ) + f ( n − 5 ) + f ( n − 6 ) f(n) = f(n-1) +f(n-2) +f(n-3)+ f(n-4) +f(n-5)+ f(n-6) f(n)=f(n1)+f(n2)+f(n3)+f(n4)+f(n5)+f(n6).
先统计出每个点数出现的次数,再除以所有点数的排列数 6 n 6^n 6n,就能求出每个点数的概率。
ps:ACwing上有这题,不过求的是次数,不是概率,差别不大


递归

定义一个求n个骰子出现和为s的次数的函数,然后遍历n到6n,求每一个点数和出现的次数。
时间复杂度太大,不能AC.

class Solution(object):
	# 求n个骰子出现和为s的次数
    def numberCount(self, n, s):
        if n < 1 or s < n or s > 6 * n: return 0
        if n == 1: return 1
        
        count = 0
        count = self.numberCount(n-1, s-1) + self.numberCount(n-1, s-2) + \
                self.numberCount(n-1, s-3) + self.numberCount(n-1, s-4) + \
                self.numberCount(n-1, s-5) + self.numberCount(n-1, s-6)
        return count
     
    # 遍历n到6n    
    def numberOfDice(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        if n < 1: return
    
        res = []
        for i in range(n, 6*n+1):
            res.append(self.numberCount(n, i))
            
        return res

循环

可以用两个数组来存储每个总数出现的次数。
在一轮循环中,第一个数组中的第n个数字表示骰子和为你出现的次数。
在下一轮循环中,加上一个新的骰子,此时和为n出现的次数为上一轮出现的和为n-1,n-2,n-3,n-4,n-5,n-6之和,即前面提到的 f ( n ) = f ( n − 1 ) + f ( n − 2 ) + f ( n − 3 ) + f ( n − 4 ) + f ( n − 5 ) + f ( n − 6 ) f(n) = f(n-1) +f(n-2)+ f(n-3)+ f(n-4) +f(n-5)+ f(n-6) f(n)=f(n1)+f(n2)+f(n3)+f(n4)+f(n5)+f(n6).
ps: 这题难倒是不难,主要是index太绕。

class Solution(object):
    def numberOfDice(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        # 为了将index与n对应上,特意把第0行空出来
        res = [[0 for i in range(6*n+1)] for i in range(n+1)]
        
        # 将第0行与第0列空出来,res[1][1:7] = [1,1,1,1,1,1]
        # 表示第一个骰子6种情况各出现一次
        for i in range(1,7):
            res[1][i] = 1
        
        # 从第二个骰子开始遍历
        for i in range(2,n+1):
        	# n个骰子之和的范围为n到6*n
            for j in range(i, 6*i+1):
            	# 当 j > 6 时,f(n) = f(n-1)+f(n-2)+f(n-3)+f(n-4) +f(n-5)+f(n-6)
            	# 当 j <= 6 时,例如3,f(n) = f(n-1)+f(n-2)
                for k in range(1, min(j+1,7)):
                    res[i][j] += res[i-1][j-k]
        
        return res[-1][n:]

关于以下这行,这里可能不太好理解:

for k in range(1, min(j+1,7)):
	res[i][j] += res[i-1][j-k]

我们可以举个具体的例子来解释:

例如n = 3 , s = 7, 这时满足s > 6.
那么在n=2时,加上一个骰子,s=7会有多少种情况呢?
答案是6种,s=1+6,s=2+5,s=3+4,s=4+3,s=5+2,s=6+1,也就是f(n) = f(n-1) + f(n-2) + f(n-3) + f(n-4) + f(n-5) + f(n-6)

但假设n=3,s = 3,这时s <= 6.
这时显然就没有6种情况,只有2种情况,即s=1+2,s=2+1

这就是为什么我们要用min(j+1,7)来区分以上两种条件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值