掷骰子模拟
难度:
标签:
题目描述
代码结果
运行时间: 58 ms, 内存: 17.4 MB
/*
* Problem: We need to calculate the number of different sequences of dice rolls given the constraint
* that each number i (1-6) cannot appear more than rollMax[i] times consecutively.
*
* Approach: We will use dynamic programming to solve this problem. We will maintain a DP table dp[i][j][k]
* where:
* - i is the number of dice rolls
* - j is the current number on the dice (1-6)
* - k is the number of times the number j has appeared consecutively
* The state transition will consider rolling a different number or continuing the streak of the current number
* up to the limit specified in rollMax.
*
* Note: Java Stream API is not very suitable for this type of problem since it's more functional in nature.
* However, for demonstration purposes, we will use streams where applicable.
*/
import java.util.Arrays;
import java.util.stream.IntStream;
public class DiceRollSimulationStream {
private static final int MOD = 1000000007;
public int dieSimulator(int n, int[] rollMax) {
int[][][] dp = new int[n + 1][6][16];
IntStream.range(0, 6).forEach(i -> dp[1][i][1] = 1);
IntStream.range(2, n + 1).forEach(roll -> {
IntStream.range(0, 6).forEach(num -> {
IntStream.range(0, 6).forEach(prevNum -> {
if (prevNum != num) {
IntStream.rangeClosed(1, rollMax[prevNum]).forEach(count -> {
dp[roll][num][1] = (dp[roll][num][1] + dp[roll - 1][prevNum][count]) % MOD;
});
} else {
IntStream.range(1, rollMax[num]).forEach(count -> {
dp[roll][num][count + 1] = (dp[roll][num][count + 1] + dp[roll - 1][num][count]) % MOD;
});
}
});
});
});
return IntStream.range(0, 6).flatMap(num ->
IntStream.rangeClosed(1, rollMax[num]).map(count -> dp[n][num][count])
).reduce(0, (a, b) -> (a + b) % MOD);
}
}
解释
方法:
这个题解使用动态规划的方法来求解。定义 f[i][j] 为投掷了 i 次骰子,且第 i 次的结果为 j+1 的不同序列的数量。数组 s[i] 表示投掷 i 次骰子总共可能的不同序列数量。初始化第一次投掷的结果 f[0] = [1, 1, 1, 1, 1, 1],因为第一次投掷每个数字都只有一种可能。对于每次投掷 i > 0,我们通过计算排除不符合 rollMax 限制的序列数量来更新 f[i][j]。如果投掷次数大于 j+1 可以连续出现的次数 mx,则需要减去那些不合法的序列。总结算后,s[i] 是所有 f[i][j] 的和,最终 s[n-1] 就是答案。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在动态规划算法中,数组 f[i][j] 的定义是什么意义?为什么它能代表在第 i 次投掷后,结果为 j+1 的不同序列数量?
▷🦆
为什么在计算 f[i][j] 时,当 i > mx 时需要减去 `s[i - mx - 1] - f[i - mx - 1][j]` 这个表达式的值?这个表达式的具体意义是什么?
▷🦆
在题解中提到当 `i == mx` 时,需要减去 1 来调整序列总数。这里的 1 指的是什么情况?为什么是减去 1 而不是其他数字?
▷🦆
如何理解题解中使用 `s[i] = sum(f[i]) % MOD` 这一步来计算当前所有可能的序列总数?这里的 sum 函数是如何工作的,以及它为什么能给出正确的结果?
▷