leetcode-掷骰子模拟

160 阅读2分钟

题目描述

有一个骰子模拟器会每次投掷的时候生成一个 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 数组,数字 12 最多连续出现一次,所以不会出现序列 (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

思路

一开始觉得是个数学题,受到其他题解的启发,才想起来可以使用动态规划。

定义状态

dp[i][j] 表示i+1次掷骰子后,最后1次为j的合法序列数量。i和j都从0开始,所以i的取值范围是[0,n),j的取值范围是[0, 5]。

状态转移方程
  • i < rollMax[j] dp[i][j] = sum(dp[i-1])
  • i = rollMax[j] dp[i][j] = sum(dp[i-1]) - 1
  • i > rollMax[j] dp[i][j] = sum(dp[i-1]) - (sum(dp[i-rollMax[j]-1]) - dp[i-rollMax[j]-1][j])

怎么理解上述的状态转移方程呢?
如果没有任何限制,那么显然,dp[i][j] = sum(dp[i-1]),我们要处理的就是要减去超出限制的情况。 当i < rollMax[j],不可能超出限制,所以不用考虑减;当i = rollMax[j],那么唯一的超出限制的情况就是从第0次到第i次,全部结果是j的情况,所以只要减1即可。最复杂的是i > rollMax[j]的情况,这种情况下,超出限制的可能就是在第i次之前,前面已经连续出现了rollMax[j]次j了,这种情况就是sum(dp[i-rollMax[j]-1]) - dp[i-rollMax[j]-1][j],这里减去dp[i-rollMax[j]-1][j]是因为第[i-rollMax[j]-1]次不能出现j,否则第i-1次就超出限制了。

Java版本代码

class Solution {

    private static final long MOD = 1000000007L;

    public int dieSimulator(int n, int[] rollMax) {
        long[][] dp = new long[n][6];
        for (int j = 0; j < 6; j++) {
            dp[0][j] = 1;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < 6; j++) {
                dp[i][j] = Arrays.stream(dp[i-1]).sum();
                if (i > rollMax[j]) {
                    dp[i][j] = (dp[i][j] - (Arrays.stream(dp[i-rollMax[j]-1]).sum() - dp[i-rollMax[j]-1][j])) % MOD;
                    while (dp[i][j] < 0) {
                        dp[i][j] += MOD;
                    }
                } else if (i == rollMax[j]) {
//                    dp[i][j] = (dp[i][j] - dp[i-rollMax[j]][j] + MOD) % MOD;
                    dp[i][j] = (dp[i][j] - 1) % MOD;
                }
//                dp[i][j] %= MOD;
            }
        }
        return (int) (Arrays.stream(dp[n-1]).sum() % MOD);
    }
}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情