掷骰子等于目标和的方法数
难度:
标签:
题目描述
代码结果
运行时间: 32 ms, 内存: 0.0 MB
/*
题目思路:
使用Java Stream API来解决这个问题相对复杂一些,因为它更适合处理集合而不是动态规划问题。
我们可以利用传统的动态规划思想,但用Stream来简化某些操作。
*/
import java.util.Arrays;
import java.util.stream.IntStream;
public class DiceRollsStream {
public static int numRollsToTarget(int n, int k, int target) {
int MOD = 1000000007;
int[][] dp = new int[n + 1][target + 1];
dp[0][0] = 1; // 初始化边界条件
IntStream.rangeClosed(1, n).forEach(i -> {
IntStream.rangeClosed(1, target).forEach(j -> {
dp[i][j] = IntStream.rangeClosed(1, k)
.filter(face -> j - face >= 0)
.map(face -> dp[i - 1][j - face])
.reduce(0, (a, b) -> (a + b) % MOD);
});
});
return dp[n][target];
}
public static void main(String[] args) {
System.out.println(numRollsToTarget(1, 6, 3)); // 输出:1
System.out.println(numRollsToTarget(2, 6, 7)); // 输出:6
System.out.println(numRollsToTarget(30, 30, 500)); // 输出:222616187
}
}
解释
方法:
该题解使用了组合数学的概念,特别是通过包容排斥原理来计算可能的骰子和。思路基于找到所有可能使得 n 个骰子的和为 target 的组合数。它使用了一个 lambda 函数 `alt` 来计算给定的序号 i 和对应的 m 值的组合数,根据奇偶性决定是否需要负号。然后,它枚举了一个范围内所有可能的组合,并对结果求和后取模 1000000007。该范围是通过从 target-1 开始向下每次减少 k 遍历到 n-2 的过程。这种方式利用数学原理简化了直接的动态规划算法的计算复杂度。
时间复杂度:
O((target-n)/k * min(n, m-n))
空间复杂度:
O(1)
代码细节讲解
🦆
为什么在解法中使用包容排斥原理,而没有选择更直观的动态规划方法?
▷🦆
在使用组合数学的方法时,您是如何确定组合函数`comb`的正确性和效率?
▷🦆
解法中的枚举范围从 target-1 到 n-2,步长为 k,这种设置的原理是什么?
▷🦆
函数`alt`中使用的负号是基于什么数学原理或者逻辑?
▷