掷骰子模拟【LC1223】
有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。
不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字
i
的次数不能超过rollMax[i]
(i
从 1 开始编号)。现在,给你一个整数数组
rollMax
和一个整数n
,请你来计算掷n
次骰子可得到的不同点数序列的数量。假如两个序列中至少存在一个元素不同,就认为这两个序列是不同的。由于答案可能很大,所以请返回 模
10^9 + 7
之后的结果。
啊啊啊激动 自己写出来了!!!
记忆化搜索+dp
-
思路:
- 由于方案数与当前为第几次掷骰子、前一个骰子值及其连续次数相关,并且整个回溯过程是有大量重复递归调用的,因此可以采用记忆化搜索和dp的方法实现
- 定义
f
(
i
,
p
r
e
,
c
o
u
n
t
)
f(i,pre,count)
f(i,pre,count) 表示第
i
−
1
i-1
i−1次骰子值为
p
r
e
pre
pre,连续掷出
p
r
e
pre
pre的次数为
c
o
u
n
t
count
count时,按照限制再掷
n-i
次骰子可得到的不同点数序列的数量,那么 f ( 0 , − 1 , 0 ) f(0,-1,0) f(0,−1,0) 即为最终结果 - 使用
dp
数组记录已经搜索过的方案数, d p [ i ] [ p r e ] [ c o u n t ] dp[i][pre][count] dp[i][pre][count]数组的含义同f函数,为第 i − 1 i-1 i−1次骰子值为 p r e pre pre,连续掷出 p r e pre pre的次数为 c o u n t count count时,按照限制掷完所有骰子可得到的不同点数序列的数量。
-
实现
- 初始时,将
dp
数组置为-1 - f函数的搜索过程为:
- 如果已经掷完所有骰子,即
i==1
时,那么返回1 - 如果当前状态已经搜索过,那么返回dp数组中的值
- 如果以上情况都不符合,那么枚举本次骰子可能的值,并将可能的方案数记录在dp数组中
- 如果骰子值与前一次相同,那么需要判断连续的次数是否小于
rollMax
- 如果骰子值与前一次不相同,那么更改骰子值,并将次数置为1,继续搜索
- 如果骰子值与前一次相同,那么需要判断连续的次数是否小于
- 如果已经掷完所有骰子,即
class Solution { public static final int MOD = (int)(1e9 + 7); int[][][] dp; int[] rollMax; int n; public int dieSimulator(int n, int[] rollMax) { this.n = n; this.rollMax = rollMax; int max = 0; for (int roll : rollMax){ max = Math.max(roll, max); } dp = new int[n][6][max + 1]; for (int i = 0; i < dp.length; i++){ for (int j = 0; j < dp[0].length; j++){ Arrays.fill(dp[i][j], -1);// 没有访问过 } } return f(0, -1, 1); } public int f(int i, int pre, int count){ if (i == n) return 1; if (pre >= 0 && count >= 0 && dp[i][pre][count] != -1) return dp[i][pre][count]; int res = 0; for (int num = 0; num < 6; num++){ if (num == pre && count >= rollMax[num]) continue; res = (res + f(i + 1, num, num == pre ? count + 1 : 1) ) % MOD; } if (pre >= 0 && count >= 0) dp[i][pre][count] = res; return res; } }
- 复杂度
- 时间复杂度:
O
(
n
m
2
k
)
O(nm^2k)
O(nm2k),其中
m
m
m为
rollMax
的长度,即 6。 k k k为rollMax
数组的最大值,状态的个数为 n m k nmk nmk,那么动态规划的时间复杂度 = 状态个数 × 单个状态的转移个数,因此时间复杂度为 O ( n m S ) O(nmS) O(nmS)。 - 空间复杂度: O ( n m k ) O(nmk) O(nmk)。
- 时间复杂度:
O
(
n
m
2
k
)
O(nm^2k)
O(nm2k),其中
m
m
m为
- 初始时,将
dp数组的形式
定义状态 d[i][j][k]
表示已经完成了 i次掷骰子,第 i次掷的是 j,并且已经连续掷了 k次 j的合法序列数。
class Solution {
private static final long MOD = (long) 1e9 + 7;
public int dieSimulator(int n, int[] rollMax) {
int m = rollMax.length; // 6
var f = new int[n][m][15];
for (int j = 0; j < m; ++j)
Arrays.fill(f[0][j], 1);
for (int i = 1; i < n; ++i)
for (int last = 0; last < m; ++last)
for (int left = 0; left < rollMax[last]; ++left) {
long res = 0;
for (int j = 0; j < m; ++j)
if (j != last) res += f[i - 1][j][rollMax[j] - 1];
else if (left > 0) res += f[i - 1][j][left - 1];
f[i][last][left] = (int) (res % MOD);
}
long ans = 0;
for (int j = 0; j < m; ++j)
ans += f[n - 1][j][rollMax[j] - 1];
return (int) (ans % MOD);
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/dice-roll-simulation/solutions/2103292/jiao-ni-yi-bu-bu-si-kao-dong-tai-gui-hua-sje6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。