[LeetCode] 1155. Number of Dice Rolls With Target Sum 擲骰子的N種方法



You have d dice and each die has f faces numbered 1, 2, ..., f.

Return the number of possible ways (out of fd total ways) modulo 109 + 7 to roll the dice so the sum of the face-up numbers equals target.

Example 1:

Input: d = 1, f = 6, target = 3
Output: 1
Explanation:
You throw one die with 6 faces.  There is only one way to get a sum of 3.

Example 2:

Input: d = 2, f = 6, target = 7
Output: 6
Explanation:
You throw two dice, each with 6 faces.  There are 6 ways to get a sum of 7:
1+6, 2+5, 3+4, 4+3, 5+2, 6+1.

Example 3:

Input: d = 2, f = 5, target = 10
Output: 1
Explanation:
You throw two dice, each with 5 faces.  There is only one way to get a sum of 10: 5+5.

Example 4:

Input: d = 1, f = 2, target = 3
Output: 0
Explanation:
You throw one die with 2 faces.  There is no way to get a sum of 3.

Example 5:

Input: d = 30, f = 30, target = 500
Output: 222616187
Explanation:
The answer must be returned modulo 10^9 + 7.

Constraints:

  • 1 <= d, f <= 30
  • 1 <= target <= 1000

這道題題說是給了d個骰子,每個骰子有f個面,現在給了一個目標值 target,問同時投出這d個骰子,共有多少種組成目標值的不同組合,結果對超大數字 1e9+7 取余。這道題其實跟之前的 Coin Change 2 硬幣找零系列很類似,只不過這里相當於確定了硬幣的總數一定要為d。萬變不離其宗,還是用動態規划 Dynamic Programming 來做,首先來考慮 dp 數組該如何定義,根據硬幣找零系列的啟發,目標值本身肯定是占一個維度的,因為這個是要求的東西,另外就是當前骰子的個數也是要考慮的因素,所以這里使用一個二維的 dp 數組,其中 dp[i][j] 表示使用i個骰子組成目標值為j的所有組合個數,大小為 d+1 by target+1,並初始化 dp[0][0] 為1。接下來就是找狀態轉移方程了,當前某個狀態 dp[i][k] 跟什么相關呢,其表示為使用i個骰子組成目標值k,那么拿最后一個骰子的情況分析,其可能會投出 [1, f] 中的任意一個數字j,那么之前的目標值就是 k-j,且用了 i-1 個骰子,其 dp 值就是 dp[i-1][k-j],當前投出的點數可以跟之前所有的情況組成一種新的組合,所以當前的 dp[i][k] 就要加上 dp[i-1][k-j],那么狀態轉移方程就呼之欲出了:

dp[i][k] = (dp[i][k] + dp[i - 1][k - j]) % M;

其中i的范圍是 [1, d],j的范圍是 [1, f],k的范圍是 [j, target],總共三個 for 循環嵌套在一起,最終返回 dp[d][target] 即可,參見代碼如下:


解法一:

class Solution {
public:
    int numRollsToTarget(int d, int f, int target) {
        int M = 1e9 + 7;
        vector<vector<int>> dp(d + 1, vector<int>(target + 1));
        dp[0][0] = 1;
        for (int i = 1; i <= d; ++i) {
            for (int j = 1; j <= f; ++j) {
                for (int k = j; k <= target; ++k) {
                    dp[i][k] = (dp[i][k] + dp[i - 1][k - j]) % M;
                }
            }
        }
        return dp[d][target];
    }
};

我們可以進行空間上的優化,由於當前使用i個骰子的狀態值依賴於使用 i-1 個骰子的狀態,所以沒必要保存所有的骰子個數的 dp 值,可以在遍歷i的時候,新建一個臨時的數組t,來保存使用i個骰子的 dp 值,並在最后交換 dp 和 t 即可,參見代碼如下:


解法二:

class Solution {
public:
    int numRollsToTarget(int d, int f, int target) {
        int M = 1e9 + 7;
        vector<int> dp(target + 1);
        dp[0] = 1;
        for (int i = 1; i <= d; ++i) {
            vector<int> t(target + 1);
            for (int j = 1; j <= f; ++j) {
                for (int k = j; k <= target; ++k) {
                    t[k] = (t[k] + dp[k - j]) % M;
                }
            }
            swap(dp, t);
        }
        return dp[target];
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1155


類似題目:

Coin Change 2

Equal Sum Arrays With Minimum Number of Operations


參考資料:

https://leetcode.com/problems/number-of-dice-rolls-with-target-sum/

https://leetcode.com/problems/number-of-dice-rolls-with-target-sum/discuss/355841/Java-Memo-DFS

https://leetcode.com/problems/number-of-dice-rolls-with-target-sum/discuss/355940/C%2B%2B-Coin-Change-2


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

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



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