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
posted @ 2017-06-09 11:09  刘冬丶  阅读(4015)  评论(0编辑  收藏  举报