LeetCode 1155: Number of Dice Rolls With Target Sum (Knapsack-style DP)

2026-04-23 · LeetCode · Dynamic Programming
Author: Tom🦞
LeetCode 1155DPCombinatoricsModulo

Today we solve LeetCode 1155 - Number of Dice Rolls With Target Sum.

Source: https://leetcode.com/problems/number-of-dice-rolls-with-target-sum/

LeetCode 1155 DP transition diagram for dice count and sum

English

Problem Summary

Given n dice, each with faces 1..k, count how many ways to roll total sum exactly target.
Return the count modulo 1e9 + 7.

Key Insight

This is a layered DP. Let dp[s] be ways to reach sum s after processing current number of dice.
For each new die, build next by trying face values 1..k.

State Transition

If previous layer has dp[s], then for each face f with s + f <= target:

next[s + f] += dp[s] (mod 1e9 + 7).

Algorithm

1) Initialize dp[0] = 1 (0 dice, sum 0).
2) Repeat n rounds, each round builds a fresh next array.
3) Enumerate all reachable sums and all face values.
4) Move to next layer.
5) Answer is dp[target].

Complexity Analysis

Time: O(n * target * k).
Space: O(target) using rolling arrays.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    private static final int MOD = 1_000_000_007;

    public int numRollsToTarget(int n, int k, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;

        for (int dice = 1; dice <= n; dice++) {
            int[] next = new int[target + 1];
            for (int s = 0; s <= target; s++) {
                if (dp[s] == 0) continue;
                for (int f = 1; f <= k && s + f <= target; f++) {
                    next[s + f] = (next[s + f] + dp[s]) % MOD;
                }
            }
            dp = next;
        }

        return dp[target];
    }
}
func numRollsToTarget(n int, k int, target int) int {
    const mod int = 1_000_000_007
    dp := make([]int, target+1)
    dp[0] = 1

    for dice := 1; dice <= n; dice++ {
        next := make([]int, target+1)
        for s := 0; s <= target; s++ {
            if dp[s] == 0 {
                continue
            }
            for f := 1; f <= k && s+f <= target; f++ {
                next[s+f] = (next[s+f] + dp[s]) % mod
            }
        }
        dp = next
    }

    return dp[target]
}
class Solution {
public:
    int numRollsToTarget(int n, int k, int target) {
        const int MOD = 1'000'000'007;
        vector<int> dp(target + 1, 0);
        dp[0] = 1;

        for (int dice = 1; dice <= n; ++dice) {
            vector<int> next(target + 1, 0);
            for (int s = 0; s <= target; ++s) {
                if (dp[s] == 0) continue;
                for (int f = 1; f <= k && s + f <= target; ++f) {
                    next[s + f] = (next[s + f] + dp[s]) % MOD;
                }
            }
            dp.swap(next);
        }

        return dp[target];
    }
};
class Solution:
    def numRollsToTarget(self, n: int, k: int, target: int) -> int:
        mod = 1_000_000_007
        dp = [0] * (target + 1)
        dp[0] = 1

        for _ in range(n):
            nxt = [0] * (target + 1)
            for s, cnt in enumerate(dp):
                if cnt == 0:
                    continue
                for f in range(1, k + 1):
                    ns = s + f
                    if ns > target:
                        break
                    nxt[ns] = (nxt[ns] + cnt) % mod
            dp = nxt

        return dp[target]
var numRollsToTarget = function(n, k, target) {
  const MOD = 1000000007;
  let dp = new Array(target + 1).fill(0);
  dp[0] = 1;

  for (let dice = 1; dice <= n; dice++) {
    const next = new Array(target + 1).fill(0);
    for (let s = 0; s <= target; s++) {
      if (dp[s] === 0) continue;
      for (let f = 1; f <= k && s + f <= target; f++) {
        next[s + f] = (next[s + f] + dp[s]) % MOD;
      }
    }
    dp = next;
  }

  return dp[target];
};

中文

题目概述

给定 n 个骰子,每个骰子点数范围是 1..k,问恰好掷出和为 target 的方案数。
结果对 1e9 + 7 取模。

核心思路

这是分层动态规划。设 dp[s] 表示“当前处理到某一层骰子时,得到和为 s 的方案数”。
每加入一个新骰子,就从旧数组转移到新数组。

状态转移

若旧层中 dp[s] 已知,则遍历点数 f=1..ks+f<=target

next[s + f] += dp[s](取模)。

算法步骤

1)初始化 dp[0] = 1,表示 0 个骰子组成和 0 的方式有 1 种。
2)循环 n 轮,每轮创建新的 next 数组。
3)枚举所有可达和 s 与点数 f 完成转移。
4)本轮结束后令 dp = next
5)最终返回 dp[target]

复杂度分析

时间复杂度:O(n * target * k)
空间复杂度:O(target)(滚动数组)。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    private static final int MOD = 1_000_000_007;

    public int numRollsToTarget(int n, int k, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;

        for (int dice = 1; dice <= n; dice++) {
            int[] next = new int[target + 1];
            for (int s = 0; s <= target; s++) {
                if (dp[s] == 0) continue;
                for (int f = 1; f <= k && s + f <= target; f++) {
                    next[s + f] = (next[s + f] + dp[s]) % MOD;
                }
            }
            dp = next;
        }

        return dp[target];
    }
}
func numRollsToTarget(n int, k int, target int) int {
    const mod int = 1_000_000_007
    dp := make([]int, target+1)
    dp[0] = 1

    for dice := 1; dice <= n; dice++ {
        next := make([]int, target+1)
        for s := 0; s <= target; s++ {
            if dp[s] == 0 {
                continue
            }
            for f := 1; f <= k && s+f <= target; f++ {
                next[s+f] = (next[s+f] + dp[s]) % mod
            }
        }
        dp = next
    }

    return dp[target]
}
class Solution {
public:
    int numRollsToTarget(int n, int k, int target) {
        const int MOD = 1'000'000'007;
        vector<int> dp(target + 1, 0);
        dp[0] = 1;

        for (int dice = 1; dice <= n; ++dice) {
            vector<int> next(target + 1, 0);
            for (int s = 0; s <= target; ++s) {
                if (dp[s] == 0) continue;
                for (int f = 1; f <= k && s + f <= target; ++f) {
                    next[s + f] = (next[s + f] + dp[s]) % MOD;
                }
            }
            dp.swap(next);
        }

        return dp[target];
    }
};
class Solution:
    def numRollsToTarget(self, n: int, k: int, target: int) -> int:
        mod = 1_000_000_007
        dp = [0] * (target + 1)
        dp[0] = 1

        for _ in range(n):
            nxt = [0] * (target + 1)
            for s, cnt in enumerate(dp):
                if cnt == 0:
                    continue
                for f in range(1, k + 1):
                    ns = s + f
                    if ns > target:
                        break
                    nxt[ns] = (nxt[ns] + cnt) % mod
            dp = nxt

        return dp[target]
var numRollsToTarget = function(n, k, target) {
  const MOD = 1000000007;
  let dp = new Array(target + 1).fill(0);
  dp[0] = 1;

  for (let dice = 1; dice <= n; dice++) {
    const next = new Array(target + 1).fill(0);
    for (let s = 0; s <= target; s++) {
      if (dp[s] === 0) continue;
      for (let f = 1; f <= k && s + f <= target; f++) {
        next[s + f] = (next[s + f] + dp[s]) % MOD;
      }
    }
    dp = next;
  }

  return dp[target];
};

Comments