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 <= 301 <= target <= 1000When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this dice problem is to try every possible combination of dice rolls. We explore every single way we can roll the dice to see if it adds up to our target number. It's like trying every single combination lock code until you find the right one.
Here's how the algorithm would work step-by-step:
def number_of_dice_rolls_with_target_sum_brute_force(
number_of_dice, number_of_faces, target_sum
):
number_of_ways = 0
def solve(dice_index, current_sum):
nonlocal number_of_ways
# If we've used all the dice, check if we've reached the target
if dice_index == number_of_dice:
if current_sum == target_sum:
number_of_ways += 1
return
# Iterate through all possible values for the current die
for face_value in range(1, number_of_faces + 1):
# Recursively call with next die and updated sum
solve(dice_index + 1, current_sum + face_value)
# Initiate recursive calls, starting with the first die
solve(0, 0)
return number_of_waysThe most efficient way to solve this problem is to break it down into smaller, overlapping subproblems and remember the solutions. This prevents us from recalculating the same thing multiple times. Think of it like building a table of answers to reuse later.
Here's how the algorithm would work step-by-step:
def num_rolls_to_target(number_of_dice, number_of_faces, target_sum):
ways_to_sum = [[0] * (target_sum + 1) for _ in range(number_of_dice + 1)]
ways_to_sum[0][0] = 1
# Base case: One way to get zero sum using zero dice.
for current_dice_count in range(1, number_of_dice + 1):
for current_target_value in range(1, target_sum + 1):
# Iterate through possible face values of the current die
for current_face_value in range(1, number_of_faces + 1):
if current_target_value >= current_face_value:
# Adding previous possible outcomes.
ways_to_sum[current_dice_count][current_target_value] += ways_to_sum[current_dice_count - 1][current_target_value - current_face_value]
# The result is the number of ways to reach the target sum
return ways_to_sum[number_of_dice][target_sum] % (10**9 + 7)| Case | How to Handle |
|---|---|
| n = 0 (no dice) | If n is 0, and the target is also 0, return 1 (one way to achieve a sum of 0 with 0 dice); otherwise, return 0. |
| k = 0 (no faces) | If k is 0, it's impossible to roll a positive sum, so return 0 unless target is also 0 (which is impossible with positive number of dice). |
| target = 0 | If target is 0 and n is greater than 0, the only way to get 0 sum is if all dice are 0 which is impossible, return 0. |
| target < 0 | Return 0 immediately as it's impossible to achieve a negative target sum with positive dice values. |
| n = 1, target > k | Return 0 because it's impossible to reach the target with a single die with 'k' faces. |
| target > n * k (target exceeds max possible sum) | Return 0 because the target is impossible to reach with the given number of dice and faces. |
| Large n, k, or target values leading to integer overflow | Apply modulo operation (10^9 + 7) after each intermediate calculation to prevent integer overflow. |
| n and k are large, potentially causing stack overflow with naive recursion | Use dynamic programming (memoization or tabulation) to avoid excessive recursion depth and potential stack overflow. |