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

Solutions

  这题目一看就很复杂,具体解析过程可参看,这里就不赘言了。一般遇到比较复杂的问题第一印象是将大问题分成很多小问题,那么如果找到比较明显的规律,可以采用递归的方式,这里第一种求解方法我们就用递归的方法。将:

class Solution:
    # 假设在n个骰子和点数和Sum的情况下,组合的次数
    def getNSumCount(self, n, n_sum):
        if n < 1 or n_sum < n or n_sum > 6 * n:
            return 0
        if n == 1:
            return 1
        n_count = self.getNSumCount(n-1, n_sum-1) + self.getNSumCount(n-1, n_sum-2) + self.getNSumCount(n-1, n_sum-3) + self.getNSumCount(n-1, n_sum-4) + self.getNSumCount(n-1, n_sum-5) + self.getNSumCount(n-1, n_sum-6) 
        return n_count
    # @param {int} n an integer
    # @return {tuple[]} a list of tuple(sum, probability)
    def dicesSum(self, n):
        # Write your code here
        if n is None or n <= 0:
            return []
        res = []
        total = 0
        for i in range(n, n*6+1):
            n_count = self.getNSumCount(n, i)
            total += n_count
            res.append([i, n_count])
        for i in range(6*n-n+1):
            res[i] = [res[i][0], round(float(res[i][1]) / total, 2)]
        return res

  然而,这里采用递归方法有大量重复,比如在算 f(6, n_sum) 和 f(6, n_sum) 时会计算很多次 f(3, n_sum) 等。可以尝试采用迭代的方式进行修改:

class Solution:
    # @param {int} n an integer
    # @return {tuple[]} a list of tuple(sum, probability)
    def dicesSum(self, n):
        # Write your code here
        if n == 0 : return None
        result = [
                [1,1,1,1,1,1],
            ]
        # if n == 1: return result[0]
        # 计算n个骰子出现的各个次数和
        for i in range(1,n):
            x = 5*(i+1)+1
            result.append([0 for _ in range(x)])
             
            for j in range(x):
                if j < 6:
                    result[i][j] = (sum(result[i-1][0:j+1]))
                elif 6 <= j <= 3*i+2:
                    result[i][j] = (sum(result[i-1][j-5:j+1]))
                else:
                    break
            left = 0
            right = len(result[i]) - 1
            while left <= right:
                result[i][right] = result[i][left]
                left += 1
                right -= 1
         
        res = result[-1]
        all = float(sum(res))
        other = []
        # 第i个元素代表骰子总和为n+i
        for i,item in enumerate(res):
            pro = item/all
            other.append([n+i,pro])
        return other

  看到现有的解法,复杂度似乎更多:

class Solution:
    # @param {int} n an integer
    # @return {tuple[]} a list of tuple(sum, probability)
    def dicesSum(self, n):
        # Write your code here
        results = []
        f = [[0 for j in xrange(6 * n + 1)] for i in xrange(n + 1)]
        
        for i in xrange(1, 7):
            f[1][i] = 1.0 / 6.0
        for i in xrange(2, n + 1):
            for j in xrange(i, 6 * n + 1):
                for k in xrange(1, 7):
                    if j > k:
                        f[i][j] += f[i - 1][j - k]
                f[i][j] /= 6.0

        for i in xrange(n, 6 * n + 1):
            results.append((i, f[n][i]))

        return results

References

  1. 20. Dices Sum-OJ
  2. LintCode Python 困难级题目 20.骰子求和 动态规划
  3. n个骰子点数和及各自出现的概率