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
This problem asks to find the number of distinct sequences of dice rolls of length n
, given constraints on the maximum consecutive rolls of each face (1 to 6). The constraints are provided in the rollMax
array.
Approach:
Both solutions presented utilize dynamic programming principles, albeit with different implementations: memoization (top-down) and iterative (bottom-up). Both share the same core idea: breaking down the problem into smaller subproblems.
Solution 1: Memoization (Top-Down)
This approach uses recursion with a memoization table (f
in the code) to store and reuse results of subproblems. The function dfs(i, j, x)
calculates the number of distinct sequences:
i
: The current roll number (0-indexed).j
: The value of the current roll (1 to 6).x
: The number of consecutive times the value j
has been rolled.The base case is when i
reaches n
(all rolls completed), returning 1 (a valid sequence). The recursive step explores all possible values for the next roll k
. If k
differs from j
, it resets the consecutive count x
to 1. If k
is the same as j
, it only continues if x
is within the allowed limit (rollMax[j-1]
). The results are memoized to prevent redundant calculations.
Time and Space Complexity Analysis (Solution 1):
n
rolls, 6 possible values for each roll, and up to the maximum consecutive rolls for each face.Solution 2: Dynamic Programming (Bottom-Up)
This approach iteratively builds a 3D DP table f[i][j][x]
, where:
i
: Roll number (1-indexed).j
: Value of the roll (1 to 6).x
: Consecutive rolls of value j
.The base case is f[1][j][1] = 1
for all j
. The iterative part then fills the table: For each i
, j
, and x
, it sums the contributions from previous rolls k
. If k != j
, the count is updated at f[i][k][1]
. If k == j
and x + 1
is within the limit, it's updated at f[i][j][x + 1]
.
Time and Space Complexity Analysis (Solution 2):
Code Examples (Python):
Solution 1 (Memoization):
from functools import cache
class Solution:
def dieSimulator(self, n: int, rollMax: List[int]) -> int:
mod = 10**9 + 7
@cache
def dfs(i, prev_roll, consecutive_count):
if i == n:
return 1
ans = 0
for curr_roll in range(1, 7):
if curr_roll != prev_roll:
ans = (ans + dfs(i + 1, curr_roll, 1)) % mod
elif consecutive_count < rollMax[curr_roll - 1]:
ans = (ans + dfs(i + 1, curr_roll, consecutive_count + 1)) % mod
return ans
return dfs(0, 0, 0)
Solution 2 (Dynamic Programming):
class Solution:
def dieSimulator(self, n: int, rollMax: List[int]) -> int:
mod = 10**9 + 7
dp = [[[0] * 16 for _ in range(7)] for _ in range(n + 1)]
for j in range(1, 7):
dp[1][j][1] = 1
for i in range(2, n + 1):
for j in range(1, 7):
for x in range(1, rollMax[j - 1] + 1):
for k in range(1, 7):
if k != j:
dp[i][k][1] = (dp[i][k][1] + dp[i - 1][j][x]) % mod
elif x + 1 <= rollMax[j - 1]:
dp[i][j][x + 1] = (dp[i][j][x + 1] + dp[i - 1][j][x]) % mod
ans = 0
for j in range(1, 7):
for x in range(1, rollMax[j - 1] + 1):
ans = (ans + dp[n][j][x]) % mod
return ans
Note: The code examples in other languages (Java, C++, Go) follow similar logic to the Python examples, adapting syntax and data structures as needed. The core DP/memoization strategy remains the same.