• LintCode Python 困难级题目 20.骰子求和 动态规划


    题目描述:

    扔 n 个骰子,向上面的数字之和为 S。给定 Given n,请列出所有可能的 S 值及其相应的概率。

     注意事项

    You do not care about the accuracy of the result, we will help you to output results.

    样例

    给定 n = 1,返回 [ [1, 0.17], [2, 0.17], [3, 0.17], [4, 0.17], [5, 0.17], [6, 0.17]]

    题目分析:

    题目:You do not care about the accuracy of the result, we will help you to output results. 

      开始还自己处理浮点数,结果题目有说不用处理。。。

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

    1.假设在n个骰子和点数和Sum的情况下,次数m = f(n,Sum) 

    2.当然n-1块骰子的情况下,一般情况下可投出Sum-1、Sum-2、Sum-3、Sum-4、Sum-5、Sum-6的情况,即留给最后一块骰子1-6的投掷空间;

      (k-1,n-1):第k个骰子投了点数1

      (k-1,n-2):第k个骰子投了点数2

      (k-1,n-3):第k个骰子投了点数3

      ....

      (k-1,n-6):第k个骰子投了点数6

      AllPro =  f(n,Sum) = f(n-1,Sum-1) + f(n-1,Sum-2) + f(n-1,Sum-3) + f(n-1,Sum-4) + f(n-1,Sum-5) + f(n-1,Sum-6) 

    3.特殊情况一,Sum - i < 1 *(n-1),即留给n-1块骰子的投掷空间已经比n-1块骰子的最小点数和还小了!

      AllPro =  f(n,Sum) = f(n-1,Sum-i) + f(n-1,Sum-i-1) + ... + f(n-1,Sum-5) + f(n-1,Sum-6) 

    4.特殊情况二,Sum - i > 6 *(n-1),即留给n-1块骰子的投掷空间已经比n-1块骰子的最大点数和还大了!

      AllPro =  f(n,Sum) = f(n-1,Sum-1) + f(n-1,Sum-2) + ... + f(n-1,Sum-i) + f(n-1,Sum-i-1) 

    例如 n = 1时:

      f(1,1) = f(1,2) = f(1,3) = f(1,4) = f(1,5) = f(1,6) = 1

    而 n = 2时:

      f(2,2) = f(1,1) = 1

      f(2,3) = f(1,2) + f(1,1) = 2

      ...

      f(2,6) = f(1,5) + f(1,4) + f(1,3) + f(1,2) + f(1,1)

      f(2,7) = f(1,6) + f(1,5) + f(1,4) + f(1,3) + f(1,2) + f(1,1) = 6

      f(2,8) =  f(1,6) + f(1,5) + f(1,4) + f(1,3) + f(1,2) = 5

      ...

    此时 f(2,2) 、f(2,3) ... f(2,6)就满足特殊情况一,而 f(2,8) 、f(2,9) ... f(2,12)就满足特殊情况二;

    递归处理会超时,这里使用数组保存每n个骰子的Sum和值可能性,再生成n+1中骰子的和值可能性分布;

    代码偷了个懒,和值分布是对称的,这里只生产一半数据,后面数据复制过去即可。

    数组result[i],因为第 i 个元素代表 i+1个骰子,所以长度为 5*(i+1) +1,

    顶峰值为第3n个元素,由于 i 为奇数时,数组长度为偶数,顶峰值有两个,3n和3n+1,同样是对称的。

    贴一下和值可能性分布数组:

    n=1  :  [1, 1, 1, 1, 1, 1]
    n=2  :  [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
    n=3  :  [1, 3, 6, 10, 15, 21, 25, 27, 27, 25, 21, 15, 10, 6, 3, 1]
    n=4  :  [1, 4, 10, 20, 35, 56, 80, 104, 125, 140, 146, 140, 125, 104, 80, 56, 35, 20, 10, 4, 1]
    n=5  :  [1, 5, 15, 35, 70, 126, 205, 305, 420, 540, 651, 735, 780, 780, 735, 651, 540, 420, 305, 205, 126, 70, 35, 15, 5, 1]


    n=6  :  [1, 6, 21, 56, 126, 252, 456, 756, 1161, 1666, 2247, 2856, 3431, 3906, 4221, 4332, 4221, 3906, 3431, 2856, 2247, 1666, 1161, 756, 456, 252, 126, 56, 21, 6, 1]


    n=7  :  [1, 7, 28, 84, 210, 462, 917, 1667, 2807, 4417, 6538, 9142, 12117, 15267, 18327, 20993, 22967, 24017, 24017, 22967, 20993, 18327, 15267, 12117, 9142, 6538, 4417, 2807, 1667, 917, 462, 210, 84, 28, 7, 1]


    n=8  :  [1, 8, 36, 120, 330, 792, 1708, 3368, 6147, 10480, 16808, 25488, 36688, 50288, 65808, 82384, 98813, 113688, 125588, 133288, 135954, 133288, 125588, 113688, 98813, 82384, 65808, 50288, 36688, 25488, 16808, 10480, 6147, 3368, 1708, 792, 330, 120, 36, 8, 1]


    n=9  :  [1, 9, 45, 165, 495, 1287, 2994, 6354, 12465, 22825, 39303, 63999, 98979, 145899, 205560, 277464, 359469, 447669, 536569, 619569, 689715, 740619, 767394, 767394, 740619, 689715, 619569, 536569, 447669, 359469, 277464, 205560,  145899, 98979, 63999, 39303, 22825, 12465, 6354, 2994, 1287, 495, 165, 45, 9, 1]


    n=10  :  [1, 10, 55, 220, 715, 2002, 4995, 11340, 23760, 46420, 85228, 147940, 243925, 383470, 576565, 831204, 1151370, 1535040, 1972630, 2446300, 2930455, 3393610, 3801535, 4121260, 4325310, 4395456, 4325310, 4121260, 3801535,3393610, 2930455, 2446300, 1972630, 1535040, 1151370, 831204, 576565, 383470, 243925, 147940, 85228, 46420, 23760, 11340, 4995, 2002, 715, 220, 55, 10, 1]

    源码:

    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 = self.round(item/all) 
                # 自己写的四舍五入算法和LintCode有出入,其实网站自身会处理数据,这里不再做处理
                pro = item/all
                other.append([n+i,pro])
            return other
            
        def round(self,num):
            # 将概率值四舍五入
            num = num*100
            num = int(2*num)/2+int(2*num)%2
            num = num/100.0
            return num
  • 相关阅读:
    让数据更精准,神器标配:热图
    运维监控大数据的提取与分析
    IT运营新世界大会:广通软件开启双态运维大时代
    持续交付的Mesos与Docker导入篇
    运算符
    Django 模型层(2)
    Django模型层
    Django的模板层
    Django的视图层
    Django的路由层(URLconf)
  • 原文地址:https://www.cnblogs.com/bozhou/p/6971081.html
Copyright © 2020-2023  润新知