Talentd
FleetCode
Make and follow sheets.Sign in

Number of Dice Rolls With Target Sum solution and explanation

MediumDynamic Programming6 min read62.2% acceptanceUpdated 9 Apr 2026
Number of Dice Rolls with Target Sum - Leetcode 1155 - PythonNeetCodeIO · 20:20 · 23K viewsWatch on YouTube
9 more videos on this problem
  1. Number of Dice Rolls with Target Sum - Leetcode 1155 - PythonNeetCodeIO · 20:20 · 23K views
  2. Number of Dice Rolls With Target Sum | Recursion | Memo | Bottom Up | Leetcode 1155codestorywithMIK · 28:40 · 7K views
  3. LeetCode 1155. Number of Dice Rolls With Target Sum Explanation and Solutionhappygirlzt · 8:21 · 4K views
  4. Leetcode 1155 Number of Dice Rolls With Target Sum | Coding Decoded SDE SheetCoding Decoded · 6:30 · 3K views
  5. 1155. Number of Dice Rolls With Target Sum | 2D DPAryan Mittal · 17:30 · 3K views
  6. Daily Leetcode #915: Oct 02, 2022 - 1155. Number of Dice Rolls With Target SumProgramming Live with Larry · 8:34 · 1K views
  7. LeetCode 1155 Number of Dice Rolls With Target SumcoderZ · 7:34 · 1K views
  8. Leetcode 1155 | Number of Dice Rolls With Target Sum | Java | Dynamic ProgrammingPessimist Programmer · 10:34 · 1K views
  9. 1155. Number of Dice Rolls With Target Sum || Python || LeetCodeThe Local Coder! · 9:29 · 698 views
  10. Number of Dice Rolls With Target Sum | LeetCode 1155 | Amazon Interview Question @LearnOverflowLearn Overflow · 19:30 · 491 views

The problem

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

Approach overview

Problem Overview: You roll n dice, each with k faces numbered from 1 to k. Count how many possible combinations produce an exact sum equal to target. The result must be returned modulo 1e9 + 7. The challenge is efficiently counting combinations without enumerating every possible roll.

Approach 1: Recursive with Memoization (Time: O(n * k * target), Space: O(n * target))

This approach models the problem as a recursive decision tree. For each die, you try every face value from 1 to k and reduce the remaining target accordingly. The state can be defined as (dice_remaining, remaining_sum). Without caching, many states repeat, causing exponential time. Memoization stores results for each state in a lookup table so repeated calls return instantly. This converts the recursion into a top‑down dynamic programming solution. The recursion depth equals the number of dice, and memoization ensures each state is computed only once.

Approach 2: Bottom-Up Dynamic Programming (Time: O(n * k * target), Space: O(n * target))

The bottom-up DP version builds the solution iteratively. Define dp[i][s] as the number of ways to get sum s using i dice. Start with the base case dp[0][0] = 1. For each dice count from 1 to n, iterate through all possible sums and accumulate contributions from the previous row: dp[i][s] += dp[i-1][s-face] for every face value 1..k. This approach avoids recursion and is often easier to reason about in interviews. The transition mirrors the recurrence used in the recursive solution but computed iteratively using a DP table.

Recommended for interviews: Start by describing the recursive state definition and recurrence to demonstrate understanding of the combinatorial structure. Then move to memoization or bottom-up dynamic programming to eliminate repeated work. Interviewers typically expect the DP formulation with complexity O(n * k * target), since brute force enumeration would be exponential and impractical.

Approach 1: Approach 1: Recursive with Memoization

We utilize recursion to explore different combinations of dice rolls to reach the target sum. For every die, we try all face values and keep track of the sum formed. To avoid redundant calculations, we use memoization.

MOD = 10**9 + 7

def numRollsToTarget(n, k, target):
    memo = [[-1] * (target + 1) for _ in range(n + 1)]
    
    def dfs(dice, target):
        if dice == 0:
            return 1 if target == 0 else 0
        if target < 0:
            return 0
        if memo[dice][target] != -1:
            return memo[dice][target]
        count = 0
        for i in range(1, k + 1):
            count = (count + dfs(dice - 1, target - i)) % MOD
        memo[dice][target] = count
        return count
    
    return dfs(n, target)

print(numRollsToTarget(2, 6, 7))  # Output: 6
import java.util.Arrays;

class Solution {
    private static final int MOD = 1000000007;
    public int numRollsToTarget(int n, int k, int target) {
        int[][] memo = new int[n + 1][target + 1];
        for (int[] m : memo) Arrays.fill(m, -1);
        return dfs(n, target, k, memo);
    }

    private int dfs(int dice, int target, int k, int[][] memo) {
        if (dice == 0) return target == 0 ? 1 : 0;
        if (target <= 0) return 0;
        if (memo[dice][target] != -1) return memo[dice][target];
        int count = 0;
        for (int i = 1; i <= k; i++) {
            count = (count + dfs(dice - 1, target - i, k, memo)) % MOD;
        }
        return memo[dice][target] = count;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.numRollsToTarget(2, 6, 7)); // Output: 6
    }
}
#include <iostream>
#include <vector>

class Solution {
public:
    int numRollsToTarget(int n, int k, int target) {
        std::vector<std::vector<int>> memo(n + 1, std::vector<int>(target + 1, -1));
        const int MOD = 1e9 + 7;
        return dfs(n, target, k, memo, MOD);
    }

private:
    int dfs(int dice, int target, int k, std::vector<std::vector<int>>& memo, const int MOD) {
        if (dice == 0) return target == 0 ? 1 : 0;
        if (target <= 0) return 0;
        if (memo[dice][target] != -1) return memo[dice][target];
        int count = 0;
        for (int i = 1; i <= k; ++i) {
            count = (count + dfs(dice - 1, target - i, k, memo, MOD)) % MOD;
        }
        return memo[dice][target] = count;
    }
};

int main() {
    Solution sol;
    std::cout << sol.numRollsToTarget(2, 6, 7) << std::endl; // Output: 6
    return 0;
}
const MOD = 1000000007;

function numRollsToTarget(n, k, target) {
    const memo = Array.from({ length: n + 1 }, () => Array(target + 1).fill(-1));

    function dfs(dice, target) {
        if (dice === 0) return target === 0 ? 1 : 0;
        if (target <= 0) return 0;
        if (memo[dice][target] !== -1) return memo[dice][target];
        let count = 0;
        for (let i = 1; i <= k; i++) {
            count = (count + dfs(dice - 1, target - i)) % MOD;
        }
        memo[dice][target] = count;
        return count;
    }
    return dfs(n, target);
}

console.log(numRollsToTarget(2, 6, 7)); // Output: 6
#include <stdio.h>
#include <string.h>
#define MOD 1000000007

int memo[31][1001];

int dfs(int dice, int target, int k) {
    if (dice == 0) return target == 0;
    if (target <= 0) return 0;
    if (memo[dice][target] != -1) return memo[dice][target];
    int count = 0;
    for (int i = 1; i <= k; ++i) {
        count = (count + dfs(dice - 1, target - i, k)) % MOD;
    }
    return memo[dice][target] = count;
}

int numRollsToTarget(int n, int k, int target) {
    memset(memo, -1, sizeof(memo));
    return dfs(n, target, k);
}

int main() {
    printf("%d\n", numRollsToTarget(2, 6, 7)); // Output: 6
    return 0;
}
using System;

public class Solution {
    private static readonly int MOD = 1000000007;
    public int NumRollsToTarget(int n, int k, int target) {
        var memo = new int[n + 1, target + 1];
        for (int i = 0; i <= n; i++)
            for (int j = 0; j <= target; j++)
                memo[i, j] = -1;
        return Dfs(n, target, k, memo);
    }

    private int Dfs(int dice, int target, int k, int[,] memo) {
        if (dice == 0) return target == 0 ? 1 : 0;
        if (target <= 0) return 0;
        if (memo[dice, target] != -1) return memo[dice, target];
        int count = 0;
        for (int i = 1; i <= k; ++i) {
            count = (count + Dfs(dice - 1, target - i, k, memo)) % MOD;
        }
        return memo[dice, target] = count;
    }

    public static void Main() {
        Solution sol = new Solution();
        Console.WriteLine(sol.NumRollsToTarget(2, 6, 7)); // Output: 6
    }
}

We define a recursive function dfs to attempt different face values for each die, decrementing dice and target step-wise. The results for already computed states are kept in a list named memo to prevent recomputation.

Complexity

Time Complexity: O(n * target * k) represents recursive state explorations.
Space Complexity: O(n * target) given the size of memoization used.

Try this approach in the editor

Approach 2: Approach 2: Dynamic Programming

Use a Dynamic Programming (DP) table where dp[i][j] represents the number of ways to achieve sum j with i dice. Initially, only dp[0][0] = 1 since there's one way to roll zero dice to get zero sum. The state transition iterates over each dice and accumulates ways using previous results.

MOD = 10**9 + 7

def numRollsToTarget(n, k, target):
    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 x in range(1, k + 1):
                if j >= x:
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - x]) % MOD
    return dp[n][target]

print(numRollsToTarget(2, 6, 7)) # Output: 6
class Solution {
    private static final int MOD = 1000000007;
    public int numRollsToTarget(int n, int k, int target) {
        int[][] dp = new int[n + 1][target + 1];
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= target; j++) {
                for (int x = 1; x <= k; x++) {
                    if (j >= x) {
                        dp[i][j] = (dp[i][j] + dp[i - 1][j - x]) % MOD;
                    }
                }
            }
        }
        return dp[n][target];
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.numRollsToTarget(2, 6, 7)); // Output: 6
    }
}
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    int numRollsToTarget(int n, int k, int target) {
        vector<vector<int>> dp(n + 1, vector<int>(target + 1, 0));
        const int MOD = 1e9 + 7;
        dp[0][0] = 1;
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j <= target; ++j) {
                for (int x = 1; x <= k; ++x) {
                    if (j - x >= 0) {
                        dp[i][j] = (dp[i][j] + dp[i - 1][j - x]) % MOD;
                    }
                }
            }
        }
        return dp[n][target];
    }
};

int main() {
    Solution sol;
    cout << sol.numRollsToTarget(2, 6, 7) << endl;
    return 0;
}
const MOD = 1000000007;

function numRollsToTarget(n, k, target) {
    const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(0));
    dp[0][0] = 1;
    for (let i = 1; i <= n; i++) {
        for (let j = 1; j <= target; j++) {
            for (let x = 1; x <= k; x++) {
                if (j >= x) {
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - x]) % MOD;
                }
            }
        }
    }
    return dp[n][target];
}

console.log(numRollsToTarget(2, 6, 7)); // Output: 6
#include <stdio.h>
#include <string.h>
#define MOD 1000000007

int numRollsToTarget(int n, int k, int target) {
    int dp[n + 1][target + 1];
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= target; ++j) {
            for (int x = 1; x <= k; ++x) {
                if (j - x >= 0) {
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - x]) % MOD;
                }
            }
        }
    }
    return dp[n][target];
}

int main() {
    printf("%d\n", numRollsToTarget(2, 6, 7));
    return 0;
}
using System;

public class Solution {
    public int NumRollsToTarget(int n, int k, int target) {
        const int MOD = 1000000007;
        int[,] dp = new int[n + 1, target + 1];
        dp[0, 0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= target; j++) {
                for (int x = 1; x <= k; x++) {
                    if (j >= x) {
                        dp[i, j] = (dp[i, j] + dp[i - 1, j - x]) % MOD;
                    }
                }
            }
        }
        return dp[n, target];
    }

    public static void Main() {
        Solution sol = new Solution();
        Console.WriteLine(sol.NumRollsToTarget(2, 6, 7)); // Output: 6
    }
}

The solution applies DP logic to process results by iterating constraints of dice and result values using accumulated states from previously computed possibilities.

Complexity

Time Complexity: O(n * target * k) for complete evaluations of rolls and outcomes.
Space Complexity: O(n * target) via state information tables.

Try this approach in the editor

Approach 3: Dynamic Programming

We define f[i][j] as the number of ways to get a sum of j using i dice. Then, we can obtain the following state transition equation:

$ f[i][j] = sum_{h=1}^{min(j, k)} f[i-1][j-h]

where h represents the number of points on the i-th die.

Initially, we have f[0][0] = 1, and the final answer is f[n][target].

The time complexity is O(n times k times target), and the space complexity is O(n times target).

We notice that the state f[i][j] only depends on f[i-1][], so we can use a rolling array to optimize the space complexity to O(target)$.

function numRollsToTarget(n: number, k: number, target: number): number {
    const f = Array.from({ length: n + 1 }, () => Array(target + 1).fill(0));
    f[0][0] = 1;
    const mod = 1e9 + 7;
    for (let i = 1; i <= n; ++i) {
        for (let j = 1; j <= Math.min(i * k, target); ++j) {
            for (let h = 1; h <= Math.min(j, k); ++h) {
                f[i][j] = (f[i][j] + f[i - 1][j - h]) % mod;
            }
        }
    }
    return f[n][target];
}
func numRollsToTarget(n int, k int, target int) int {
	const mod int = 1e9 + 7
	f := make([][]int, n+1)
	for i := range f {
		f[i] = make([]int, target+1)
	}
	f[0][0] = 1
	for i := 1; i <= n; i++ {
		for j := 1; j <= min(target, i*k); j++ {
			for h := 1; h <= min(j, k); h++ {
				f[i][j] = (f[i][j] + f[i-1][j-h]) % mod
			}
		}
	}
	return f[n][target]
}
impl Solution {
    pub fn num_rolls_to_target(n: i32, k: i32, target: i32) -> i32 {
        let _mod = 1_000_000_007;
        let n = n as usize;
        let k = k as usize;
        let target = target as usize;
        let mut f = vec![vec![0; target + 1]; n + 1];
        f[0][0] = 1;

        for i in 1..=n {
            for j in 1..=target.min(i * k) {
                for h in 1..=j.min(k) {
                    f[i][j] = (f[i][j] + f[i - 1][j - h]) % _mod;
                }
            }
        }

        f[n][target]
    }
}
Try this approach in the editor

Approach 4: Default Approach

function numRollsToTarget(n: number, k: number, target: number): number {
    const f = Array(target + 1).fill(0);
    f[0] = 1;
    const mod = 1e9 + 7;
    for (let i = 1; i <= n; ++i) {
        const g = Array(target + 1).fill(0);
        for (let j = 1; j <= Math.min(i * k, target); ++j) {
            for (let h = 1; h <= Math.min(j, k); ++h) {
                g[j] = (g[j] + f[j - h]) % mod;
            }
        }
        f.splice(0, target + 1, ...g);
    }
    return f[target];
}
func numRollsToTarget(n int, k int, target int) int {
	const mod int = 1e9 + 7
	f := make([]int, target+1)
	f[0] = 1
	for i := 1; i <= n; i++ {
		g := make([]int, target+1)
		for j := 1; j <= min(target, i*k); j++ {
			for h := 1; h <= min(j, k); h++ {
				g[j] = (g[j] + f[j-h]) % mod
			}
		}
		f = g
	}
	return f[target]
}
impl Solution {
    pub fn num_rolls_to_target(n: i32, k: i32, target: i32) -> i32 {
        let _mod = 1_000_000_007;
        let n = n as usize;
        let k = k as usize;
        let target = target as usize;
        let mut f = vec![0; target + 1];
        f[0] = 1;

        for i in 1..=n {
            let mut g = vec![0; target + 1];
            for j in 1..=target {
                for h in 1..=j.min(k) {
                    g[j] = (g[j] + f[j - h]) % _mod;
                }
            }
            f = g;
        }

        f[target]
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Recursive with Memoization

Time Complexity: O(n * target * k) represents recursive state explorations.
Space Complexity: O(n * target) given the size of memoization used.

Approach 2: Dynamic Programming

Time Complexity: O(n * target * k) for complete evaluations of rolls and outcomes.
Space Complexity: O(n * target) via state information tables.

Dynamic Programming–
Default Approach–

Complexity analysis

ApproachTimeSpaceWhen to Use
Recursive with MemoizationO(n * k * target)O(n * target)When you want a natural recursive formulation and easy state definition
Bottom-Up Dynamic ProgrammingO(n * k * target)O(n * target)Preferred iterative solution for interviews and production code
Space-Optimized DPO(n * k * target)O(target)When memory usage matters and only the previous dice state is needed

Frequently asked questions

Is Number of Dice Rolls With Target Sum easy or hard?

LeetCode classifies this problem as Medium difficulty. The recurrence relation is straightforward, but recognizing the dynamic programming state and handling modulo arithmetic makes it slightly more challenging for beginners.

Number of Dice Rolls With Target Sum Python/Java solution

Both Python and Java implementations typically use dynamic programming. Python solutions often use recursion with functools.lru_cache or a DP list, while Java commonly uses a 2D int array dp[n+1][target+1] with modulo arithmetic for large counts.

How to solve Number of Dice Rolls With Target Sum in O(n)?

The problem cannot be solved in strict O(n) time because each dice roll introduces k possible face values and multiple target sums must be evaluated. The best known solution uses dynamic programming with O(n * k * target) complexity by computing combinations incrementally.

What is the best approach for Number of Dice Rolls With Target Sum?

Dynamic Programming is the most efficient approach. Define dp[i][s] as the number of ways to reach sum s using i dice. For each state, iterate over all k faces and accumulate transitions from dp[i-1][s-face]. This reduces the exponential brute force search to O(n * k * target) time.

Is Number of Dice Rolls With Target Sum asked at Google/Amazon/Meta?

Dynamic programming counting problems like this appear frequently in interviews at companies such as Amazon, Google, and Meta. Variations often involve dice, coin change, or ways to reach a target sum using constrained choices.

What data structure is used in Number of Dice Rolls With Target Sum?

The main data structure is a dynamic programming table or memoization cache indexed by dice count and remaining sum. Implementations usually use a 2D array or dictionary for memoization and sometimes reduce space to a 1D array.

What is the time complexity of Number of Dice Rolls With Target Sum?

The optimal dynamic programming solution runs in O(n * k * target) time. There are n dice states and up to target sums, and each state iterates through k possible face values. Space complexity is typically O(n * target), which can be optimized to O(target).

Comments

Now write it yourselfClose this page and solve Number of Dice Rolls With Target Sum in the editor, without looking.
Practise Number of Dice Rolls With Target Sum