掷骰子等于目标和的方法数【LC1155】
这里有
n
个一样的骰子,每个骰子上都有k
个面,分别标号为1
到k
。给定三个整数
n
,k
和target
,返回可能的方式(从总共kn
种方式中)滚动骰子的数量,使正面朝上的数字之和等于target
。答案可能很大,你需要对
109 + 7
取模 。
第一眼很像背包
记忆化
-
思路
-
题意转化:求出选择 n n n个值为 1 ∼ k 1\sim k 1∼k的数,和为
target
的方案数 -
子问题:如果第一个数选择1,那么问题变为求出选择 n − 1 n-1 n−1个值为 1 ∼ k 1\sim k 1∼k的数,和为
target-1
的方案数。该问题是相似的子问题,因此可以使用记忆化或者dp解决 -
状态定义:定义递归函数 d f s ( n , t a r g e t ) dfs(n,target) dfs(n,target)表示选择 n n n个值为 1 ∼ k 1\sim k 1∼k的数,和为
target
的方案数 -
递归过程:枚举每轮可以选择的数 i i i
d p [ n ] [ t a r g e t ] = ∑ i = 1 k d p [ n − 1 ] [ t a r g e t − i ] dp[n][target]= \sum_{i=1}^{k}dp[n-1][target-i] dp[n][target]=i=1∑kdp[n−1][target−i] -
递归边界
- d f s ( 0 , j ) = 0 dfs(0,j)=0 dfs(0,j)=0
- d f s ( 0 , 0 ) = 1 dfs(0,0)=1 dfs(0,0)=1
-
-
实现
class Solution { public static final int MOD = (int)(1e9 + 7); int[][] dp; int k; public int numRollsToTarget(int n, int k, int target) { // 选择n个数,值为1-k,和为target的方案数 if (n * k < target) return 0; this.k = k; this.dp = new int[n + 1][target + 1]; for (int i = 1; i <= n; i++){ Arrays.fill(dp[i], -1); } dp[0][0] = 1; return dfs(n, target); } public int dfs(int n, int target){ if (dp[n][target] != -1) return dp[n][target]; int res = 0; for (int i = 1; i <= Math.min(target, k); i++){ res = (res + dfs(n - 1, target - i)) % MOD; } dp[n][target] = res; return res; } }
-
复杂度
- 时间复杂度: O ( n ∗ t a r g e t ∗ k ) O(n*target*k) O(n∗target∗k),其中 n 为 stones 的长度。动态规划的时间复杂度 = 状态个数 × 单个状态的计算时间。这里状态个数为 O ( n ∗ t a r g e t ) O(n*target) O(n∗target),单个状态的计算时间为 O ( k ) O(k) O(k) ,因此时间复杂度为 O ( n 3 / k ) O(n^3/k) O(n3/k)
- 空间复杂度: O ( n ∗ t a r g e t ) O(n*target) O(n∗target)
-
-
优化:每个数至少取1, 因此可以将target减少n,每个数的范围减小1
递推
-
实现
class Solution { public static final int MOD = (int)(1e9 + 7); public int numRollsToTarget(int n, int k, int target) { // 选择n个数,值为1-k,和为target的方案数 if (n * k < target) return 0; int[][] dp = new int[n + 1][target + 1]; dp[0][0] = 1; for (int i = 1; i <= n; i++){ for (int j = 1; j <= Math.min(k, target); j++){ for (int v = j; v <= target; v++){ dp[i][v] = (dp[i][v] + dp[i - 1][v - j]) % MOD; } } } return dp[n][target]; } }