• [LeetCode] 1223. Dice Roll Simulation 掷骰子模拟



    A die simulator generates a random number from 1 to 6 for each roll. You introduced a constraint to the generator such that it cannot roll the number i more than rollMax[i] (1-indexed) consecutive times.

    Given an array of integers rollMax and an integer n, return the number of distinct sequences that can be obtained with exact n rolls. Since the answer may be too large, return it modulo 109 + 7.

    Two sequences are considered different if at least one element differs from each other.

    Example 1:

    Input: n = 2, rollMax = [1,1,2,2,2,3]
    Output: 34
    Explanation: There will be 2 rolls of die, if there are no constraints on the die, there are 6 * 6 = 36 possible combinations. In this case, looking at rollMax array, the numbers 1 and 2 appear at most once consecutively, therefore sequences (1,1) and (2,2) cannot occur, so the final answer is 36-2 = 34.
    

    Example 2:

    Input: n = 2, rollMax = [1,1,1,1,1,1]
    Output: 30
    

    Example 3:

    Input: n = 3, rollMax = [1,1,1,2,2,3]
    Output: 181
    

    Constraints:

    • 1 <= n <= 5000
    • rollMax.length == 6
    • 1 <= rollMax[i] <= 15

    这道题让模拟摇骰子的过程,每次随机摇出1到6之间的数字,但是给了一个限定条件,说是数字i不能连续摇出超过 rollMax[i-1] 次,现在让摇n次骰子,问可以摇出多少种不同的组合,结果对一个超大数取余。看到这种结果对一个超大数字取余的题,基本上可以直接放弃暴力破解的想法,直接无脑上动态规划 Dynamic Programming。现在来想一想,假如没有 rollMax 的限制条件,那么摇n次骰子可能出现组合总数就是6的n次方,现在有了这个限制条件,问题就变得复杂了,不然也对不起这 Hard 的身价。既然是要限定某个数字连续出现的次数不能超过一个限定值,则最后一次摇出的数字就是一个必备的信息,需要加到当前状态里,当然还需要知道当前是第几次摇,这样就需要两个变量来定义一个状态,于是用一个二维的 DP 数组,其中 dp[i][j] 表示摇了第i次且摇出的数字是 j+1 时的总的组合次数,于是所求的结果就是将所有的 dp[n][j] 加起来即可。

    接下来的难点就是求状态转移方程了,对于任意一个状态 dp[i][j],不考虑限制条件的话,第i次摇出数字 j+1 的情况组合应该等于第 i-1 次摇出任意一个数字的情况总和,但是由于 rollMax[j] 第存在,不能使得数字 j+1 连续摇出 rollMax[j] 次,所以当前摇了第几次也很关键,可以另外用一个变量k从1遍历到i,这里当 k 小于 rollMax[j] 时,表示不受限制,可以连续摇出数字 j+1,但是这里我们不是直接加上上一轮所有的结果(为了方便知道每一轮的组合总数,用另一个数组 sum 来表示,其中 sum[i] 表示第i次摇的所有组合个数),而是要减去 dp[i-k][j],因为 dp[i-k][j] 中可能包括了当前不能加上的组合,比如当前数字 j+1 的限制次数是2次,而 dp[i-1][j] 里包括了 j+1 出现2次的情况,此时 dp[i][j] 直接加上这个会出现问题,所以要减去 dp[i-1][j] 的值,然后再去看 k=2 的情况,若超过次数限制了,就不加了,若没有,则还是同样的 logic,加上 sum[i-2],再减去 dp[i-2][j],最终到i等于k时,则 dp[i-k][j] 为0,而 sum[i-k] 是1,即可以将第一次摇出数字 j+1 的情况加上,不会漏掉任何一种情况,参见代码如下:


    class Solution {
    public:
        int dieSimulator(int n, vector<int>& rollMax) {
            int M = 1e9 + 7;
            vector<vector<long>> dp(n + 1, vector<long>(6));
            vector<long> sum(n + 1);
            sum[0] = 1;
            for (int i = 1; i <= n; ++i) {
                for (int j = 0; j < 6; ++j) {
                    for (int k = 1; k <= rollMax[j] && i >= k; ++k) {
                        dp[i][j] = (dp[i][j] + sum[i - k] - dp[i - k][j] + M) % M;
                    }
                    sum[i] = (sum[i] + dp[i][j]) % M;
                }
            }
            return sum[n];
        }
    };
    

    Github 同步地址:

    https://github.com/grandyang/leetcode/issues/1223


    参考资料:

    https://leetcode.com/problems/dice-roll-simulation/

    https://leetcode.com/problems/dice-roll-simulation/discuss/403756/Java-Share-my-DP-solution

    https://leetcode.com/problems/dice-roll-simulation/discuss/423808/C%2B%2BJava-two-dimensional-DP-with-explanation


    LeetCode All in One 题目讲解汇总(持续更新中...)


    喜欢请点赞,疼爱请打赏❤️~.~


    微信打赏


    Venmo 打赏

  • 相关阅读:
    讲一下创业公司的技术架构演进
    Nginx二级域名配置
    Java分页下载
    Solr6+IKAnalyzer分词环境搭建
    消息发送平台简单架构设计
    优先级线程池实现
    Spring缓存框架原理浅谈
    Archaius 原理
    SpringCloud+Consul 服务注册与服务发现
    EndPoint详解
  • 原文地址:https://www.cnblogs.com/grandyang/p/15323679.html
Copyright © 2020-2023  润新知