题目:掷骰子模拟
- 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。
- 不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字
i
的次数不能超过rollMax[i](i
从 1 开始编号)。
现在,给你一个整数数组rollMax
和一个整数n
,请你来计算掷n
次骰子可得到的不同点数序列的数量。
假如两个序列中至少存在一个元素不同,就认为这两个序列是不同的。由于答案可能很大,所以请返回 模10^9 + 7
之后的结果。
示例 1:
输入:n = 2, rollMax = [1,1,2,2,2,3]
输出:34
解释:我们掷 2 次骰子,如果没有约束的话,共有 6 * 6 = 36 种可能的组合。但是根据 rollMax 数组,数字 1 和 2 最多连续出现一次,所以不会出现序列 (1,1) 和 (2,2)。因此,最终答案是 36-2 = 34。
示例 2:
输入:n = 2, rollMax = [1,1,1,1,1,1]
输出:30
示例 3:
输入:n = 3, rollMax = [1,1,1,2,2,3]
输出:181
提示:
- 1 <= n <= 5000
- rollMax.length == 6
- 1 <= rollMax[i] <= 15
来源:力扣(LeetCode)第1223题
分析:
- 这是一道动态规划题
- 题目有一点要注意,是连续投掷次数不能超过i,而不是总次数不能超过i。
- 我们设三个状态,i,j,k。i代表第i轮,j代表投掷了第j个数,k代表第j个数已经连续投出多少次了。
- 当k等于1,说明之前一轮没有投出j,因此次数是之前一轮没有投出j的次数之和。
- 当k大于1时,说明之前一轮投的也是k,所以次数就是之前一轮投的是j的时候k-1次。
代码:
class Solution {
private long mod = (long)1e9 + 7;
public int dieSimulator(int n, int[] rollMax) {
long[][][] dp = new long[n][6][16];
for (int j = 0; j < 6; j++) dp[0][j][1] = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < 6; j++) {
for (int l = 0; l < 6; l++) {
for (int k = 1; k <= rollMax[l]; k++) {
if (l == j) continue;
dp[i][j][1] += dp[i-1][l][k];
dp[i][j][1] %= mod;
}
}
for (int k = 2; k <= rollMax[j]; k++) dp[i][j][k] = dp[i-1][j][k-1];
}
}
long ans = 0;
for (int j = 0; j < 6; j++) for (int k = 1; k <= 15; k++) {ans += dp[n-1][j][k];ans %= mod;}
return (int)ans;
}
}