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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM