题目:掷骰子模拟

  • 有一个骰子模拟器会每次投掷的时候生成一个 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题

链接:https://leetcode-cn.com/problems/dice-roll-simulation

分析:

  • 这是一道动态规划题
  • 题目有一点要注意,是连续投掷次数不能超过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;
    }
}