【劍指offer】面試題43:n個骰子的點數
阿新 • • 發佈:2019-01-28
第一種思路是,每個骰子的點數從最小到最大,假設為1-6,那麼所有的骰子從最小1開始,我們假設一種從左向右的排列,右邊的最低,索引從最低開始,判斷和的情況。
def setTo1(dices, start, end): for i in range(start, end): dices[i] = 1 def probability(n, s, dmax = 6, dmin = 1): if s < n * dmin or s > n * dmax : return 0 dices = [1] * n i = n - 1 total = 0 while i >= 0: curSum = sum(dices) if curSum == s: print dices total += 1 # find first one that can +1 for j in range(i, -1, -1): if dices[j] < dmax and s - sum(dices[0:j+1]) >= n - j*dmin: dices[j] += 1 setTo1(dices, j + 1, n) i = n - 1 break else: i -= 1 elif curSum < s: if dices[i] < dmax: dices[i] += 1 i = n - 1 else: i -= 1 print "total = {0}, prob = {1}%".format(total, total*100/dmax**n) return total
若當前和小於s,則檢驗當前索引處的骰子能否增加1,若能,則增加,否則檢視其前面的能否增加。若相等,那麼我們統計資訊後,要變化當前的情形,以便處理下一種情況,因為 索引是從低位開始到當前位的,所以我們從當前索引開始,向前找能繼續增加的骰子,這裡的判斷標準是當前骰子的點數小於最小值,而且要保證其後的骰子的最小值為1,比如 1,4,1, s = 6, 當前索引指向4, 這裡的4雖然小於最大點數6, 但若其再加一,第三個骰子就的為0,這不符合要求。若找到可以加一的骰子,那就將該骰子點數加1, 將其後的骰子都置為1,索引回到最後,開始重新加起。如:1,1,6,s = 8, 索引指向6, 修改後為1,2,1,索引指向最後的1,。若沒有找到可以再增加的骰子,那麼就結束。如6,1,1,s = 8。
其實若給定的n不大的話,我們可以設一個n位整數,從n個1開始,逐次加一,來判斷各個位的和是否滿足要求,直到達到最大值,n個6。
這個問題其實動態規劃的特點很明顯。
''' @ state function: dp[i, j]: the total cases of sum = j, composed by i dices @ state tranfor function: dp[i, j] = sum(dp[i - 1, j - k]) for k in [dmin, dmax] @ dp[i, j] = 0, j > i * dmax or j < i * dmin @ init condition: dp[1, k] = 1, for k in [dmin, dmax], dp[1, k] = 0, for other k ''' def dp_probability(n, s, dmax = 6, dmin = 1): if s < n * dmin or s > n * dmax : return 0 dp1 = [0] * (n * dmax + 1) #init dp[1, :] for i in range(1, dmax + 1): dp1[i] = 1 # i: the number of dices for i in range(2, n + 1): dp2 = [0] * (n * dmax + 1) # j: range of i dices for j in range(dmin * i, dmax * i + 1): # k: range of new added dice for k in range(dmin, dmax + 1): if j > k : dp2[j] += dp1[j - k] print dp2 dp1 = dp2 print "total = {0}, prob = {1}%".format(dp2[s], dp2[s]*100/dmax**n) return dp2[s]