【每日算法Day 98】慈善賭神godweiyang教你算骰子點數概率!

題目鏈接

LeetCode 面試題60. n個骰子的點數[1]

題目描述

n 個骰子扔在地上,所有骰子朝上一面的點數之和爲 s。輸入 n,打印出 s 的所有可能的值出現的概率。

你需要用一個浮點數數組返回答案,其中第 i 個元素代表這 n 個骰子所能擲出的點數集合中第 i 小的那個的概率。

說明:

  • 1 <= n <= 11

示例1

        輸入:
1
輸出:
[0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
      

示例2

        輸入:
2
輸出:
[0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]
      

題解

dp[n][s] 表示投擲 n 個骰子,點數爲 s 的方法數。那麼可以根據最後一個骰子的點數情況(16),遞歸進行計算:

dp[n][s] = \sum_{i=1}^{6}{dp[n-1][s-i]} \\

當然還得加一些約束,例如 n-1 個骰子的點數範圍是 [n-1, 6(n-1)] ,所以一定有 n-1 \le s-i \le 6(n-1) ,即 s-6(n-1) \le i \le s-(n-1)。所以綜上 i 的範圍是 \max{\{1, s-6(n-1)\}} \le i \le \min{\{6, s-(n-1)\}},最後的轉移方程就是:

dp[n][s] = \sum_{i=\max{\{1, s-6(n-1)\}}}^{\min{\{6, s-(n-1)\}}}{dp[n-1][s-i]} \\

但是,考慮到在計算 n-1 個骰子時,如果 i < s-6(n-1) ,那麼 s-i > 6(n-1) ,也就是 dp[n-1][s-i] 是根本不會被計算的。所以初始化的時候如果都是 0 ,那麼就不用管這個下界了,也就是轉移方程爲:

dp[n][s] = \sum_{i=1}^{\min{\{6, s-(n-1)\}}}{dp[n-1][s-i]} \\

此外,因爲每次計算只會用到 n-1 個骰子的方法數,所以第一個維度可以省去。但是注意計算的時候 s 就得逆序遍歷了,這樣纔不會覆蓋掉 n-1 個骰子的方案數,造成後面的計算錯誤。

最後答案就是 \frac{dp[n][s]}{6^n}

代碼

動態規劃+空間優化(c++)

        class Solution {
public:
    vector<double> twoSum(int n) {
        vector<int> dp(6*n+1, 0);
        for (int i = 1; i <= 6; ++i) dp[i] = 1;
        for (int i = 2; i <= n; ++i) {
            for (int s = 6*i; s >= i; --s) {
                dp[s] = 0;
                for (int j = 1; j <= min(6, s-i+1); ++j) {
                    dp[s] += dp[s-j];
                }
            }
        }
        double total = pow(6, n);
        vector<double> res;
        for (int s = n; s <= 6*n; ++s) {
            res.push_back(dp[s]/total);
        }
        return res;
    }
};

      

參考資料

[1]

LeetCode 面試題60. n個骰子的點數: leetcode-cn.com/problem

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章