You have n
dice, and each dice has k
faces numbered from 1
to k
.
Given three integers n
, k
, and target
, return the number of possible ways (out of the kn
total ways) to roll the dice, so the sum of the face-up numbers equals target
. Since the answer may be too large, return it modulo 109 + 7
.
Example 1:
Input: n = 1, k = 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: n = 2, k = 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: n = 30, k = 30, target = 500 Output: 222616187 Explanation: The answer must be returned modulo 109 + 7.
Constraints:
1 <= n, k <= 30
1 <= target <= 1000
Given n
dice, each with k
faces numbered from 1 to k
, and a target sum target
, find the number of possible ways to roll the dice such that the sum of the face-up numbers equals target
. The result should be modulo 10<sup>9</sup> + 7
.
This problem can be efficiently solved using dynamic programming. We'll create a 2D array (or a 1D array optimized for space) to store the number of ways to achieve a given sum with a given number of dice.
Approach:
Initialization: Create a DP array dp
.
dp[i][j]
represents ways to get sum j
using i
dice), initialize dp[0][0] = 1
.dp[j]
represents ways to get sum j
), initialize dp[0] = 1
.Iteration: Iterate through the number of dice (i
) from 1 to n
. For each die:
j
) from 1 to target
.j
, iterate through possible outcomes (h
) of the current die (from 1 to k
or min(j, k)
).dp[i][j]
(or dp[j]
in the optimized version) by adding the number of ways to achieve the remaining sum (j - h
) using the previous dice (i - 1
). This is done modulo 10<sup>9</sup> + 7
to avoid integer overflow.Result: The final answer will be dp[n][target]
(or dp[target]
in the optimized version).
Time and Space Complexity:
The optimized 1D approach significantly reduces space complexity because we only need to store results for the current sum at any given point.
Python (2D DP):
class Solution:
def numRollsToTarget(self, n: int, k: int, target: int) -> int:
mod = 10**9 + 7
dp = [[0] * (target + 1) for _ in range(n + 1)]
dp[0][0] = 1
for i in range(1, n + 1):
for j in range(1, target + 1):
for h in range(1, min(j, k) + 1):
dp[i][j] = (dp[i][j] + dp[i - 1][j - h]) % mod
return dp[n][target]
Python (1D DP Optimized):
class Solution:
def numRollsToTarget(self, n: int, k: int, target: int) -> int:
mod = 10**9 + 7
dp = [0] * (target + 1)
dp[0] = 1
for i in range(1, n + 1):
new_dp = [0] * (target + 1)
for j in range(1, target + 1):
for h in range(1, min(j, k) + 1):
new_dp[j] = (new_dp[j] + dp[j - h]) % mod
dp = new_dp
return dp[target]
Java (2D DP): Similar structure to the Python 2D DP solution.
Java (1D DP Optimized): Similar structure to the Python 1D DP optimized solution.
C++, Go, TypeScript, and Rust: The implementation in these languages will follow a similar pattern to the Python examples, adapting the syntax and data structures accordingly. (Refer to the original response for complete code examples in these languages).
The optimized 1D approach reduces space complexity. Let's trace how it works:
dp = [0] * (target + 1)
, dp[0] = 1
.i
). The inner loops calculate the number of ways to achieve each sum (j
) using i
dice.new_dp
temporarily stores the results for the current iteration.i
, dp
is updated to new_dp
, overwriting the previous results—this is the key to saving space.This dynamic programming approach efficiently calculates the number of ways to obtain the target sum by building upon solutions for smaller subproblems (fewer dice and smaller sums). The modulo operation prevents integer overflow, ensuring that the results remain within a manageable range.