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