[LeetCode] 1223. Dice Roll Simulation 擲骰子模擬



A die simulator generates a random number from 1 to 6 for each roll. You introduced a constraint to the generator such that it cannot roll the number i more than rollMax[i] (1-indexed) consecutive times.

Given an array of integers rollMax and an integer n, return the number of distinct sequences that can be obtained with exact n rolls. Since the answer may be too large, return it modulo 109 + 7.

Two sequences are considered different if at least one element differs from each other.

Example 1:

Input: n = 2, rollMax = [1,1,2,2,2,3]
Output: 34
Explanation: There will be 2 rolls of die, if there are no constraints on the die, there are 6 * 6 = 36 possible combinations. In this case, looking at rollMax array, the numbers 1 and 2 appear at most once consecutively, therefore sequences (1,1) and (2,2) cannot occur, so the final answer is 36-2 = 34.

Example 2:

Input: n = 2, rollMax = [1,1,1,1,1,1]
Output: 30

Example 3:

Input: n = 3, rollMax = [1,1,1,2,2,3]
Output: 181

Constraints:

  • 1 <= n <= 5000
  • rollMax.length == 6
  • 1 <= rollMax[i] <= 15

這道題讓模擬搖骰子的過程,每次隨機搖出1到6之間的數字,但是給了一個限定條件,說是數字i不能連續搖出超過 rollMax[i-1] 次,現在讓搖n次骰子,問可以搖出多少種不同的組合,結果對一個超大數取余。看到這種結果對一個超大數字取余的題,基本上可以直接放棄暴力破解的想法,直接無腦上動態規划 Dynamic Programming。現在來想一想,假如沒有 rollMax 的限制條件,那么搖n次骰子可能出現組合總數就是6的n次方,現在有了這個限制條件,問題就變得復雜了,不然也對不起這 Hard 的身價。既然是要限定某個數字連續出現的次數不能超過一個限定值,則最后一次搖出的數字就是一個必備的信息,需要加到當前狀態里,當然還需要知道當前是第幾次搖,這樣就需要兩個變量來定義一個狀態,於是用一個二維的 DP 數組,其中 dp[i][j] 表示搖了第i次且搖出的數字是 j+1 時的總的組合次數,於是所求的結果就是將所有的 dp[n][j] 加起來即可。

接下來的難點就是求狀態轉移方程了,對於任意一個狀態 dp[i][j],不考慮限制條件的話,第i次搖出數字 j+1 的情況組合應該等於第 i-1 次搖出任意一個數字的情況總和,但是由於 rollMax[j] 第存在,不能使得數字 j+1 連續搖出 rollMax[j] 次,所以當前搖了第幾次也很關鍵,可以另外用一個變量k從1遍歷到i,這里當 k 小於 rollMax[j] 時,表示不受限制,可以連續搖出數字 j+1,但是這里我們不是直接加上上一輪所有的結果(為了方便知道每一輪的組合總數,用另一個數組 sum 來表示,其中 sum[i] 表示第i次搖的所有組合個數),而是要減去 dp[i-k][j],因為 dp[i-k][j] 中可能包括了當前不能加上的組合,比如當前數字 j+1 的限制次數是2次,而 dp[i-1][j] 里包括了 j+1 出現2次的情況,此時 dp[i][j] 直接加上這個會出現問題,所以要減去 dp[i-1][j] 的值,然后再去看 k=2 的情況,若超過次數限制了,就不加了,若沒有,則還是同樣的 logic,加上 sum[i-2],再減去 dp[i-2][j],最終到i等於k時,則 dp[i-k][j] 為0,而 sum[i-k] 是1,即可以將第一次搖出數字 j+1 的情況加上,不會漏掉任何一種情況,參見代碼如下:


class Solution {
public:
    int dieSimulator(int n, vector<int>& rollMax) {
        int M = 1e9 + 7;
        vector<vector<long>> dp(n + 1, vector<long>(6));
        vector<long> sum(n + 1);
        sum[0] = 1;
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < 6; ++j) {
                for (int k = 1; k <= rollMax[j] && i >= k; ++k) {
                    dp[i][j] = (dp[i][j] + sum[i - k] - dp[i - k][j] + M) % M;
                }
                sum[i] = (sum[i] + dp[i][j]) % M;
            }
        }
        return sum[n];
    }
};

Github 同步地址:

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


參考資料:

https://leetcode.com/problems/dice-roll-simulation/

https://leetcode.com/problems/dice-roll-simulation/discuss/403756/Java-Share-my-DP-solution

https://leetcode.com/problems/dice-roll-simulation/discuss/423808/C%2B%2BJava-two-dimensional-DP-with-explanation


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


免責聲明!

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



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