• [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.

    Two sequences are considered different if at least one element differs from each other. Since the answer may be too large, return it modulo 10^9 + 7.

    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

    Dynamic Programming Solution 1

    State definition: dp[i][j]: the total number of different sequences of i rolls with the the last number being j.  Init: dp[1][j] = 1, for j in [0, 5]. (1-indexed mapped to 0-indexed).  Answer: sum of dp[n][j] for j in [0, 5].

    State transition analysis: for dp[i][j], there can be two cases: either the last rolled number is different with the previous number or is the same with the previous one.

    For the 1st case,  simply iterate j from 0 to 5 and add all dp[i - 1][k] where k != j to dp[i][j]. 

    For the 2nd case,  we know that there are already two consecutive rolled numbers of j. We can have at most rollMax[j] consecutive j. Since rollMax[j] is at most 15, we can just add all the sequences that end with 2, 3, 4... rollMax[j] consecutive j. 

    A few key points in doing so: 

    1. for a fixed consecutive number of j, say cnt, we need to add all dp[i - cnt][t] where t != j. This represents all sequences that ends with cnt exact consecutive j. 

    2. There are two scenarios that terminate the sum. Either we exceed the limit of rollMax[j] or we run out of rolls, i.e, the current number of rolls is <= rollMax[j]. If exceeding rollMax[j], simply stop; if we are out of rolls, we need to add 1 to dp[i][j] to count the all js in all i rolls case. This is because when we have 0 roll, we don't have other 5 numbers that are different with j to choose from. It is just one sequence that only has j in it.

    The runtime is O(n * 6 * 15 * 6).  space complexity is O(n * 6).

    Mistakes made: when summing the final result res, use res = (res + dp[n][i]) % mod;   res += dp[n][i] % mod; is incorrect because it does not apply modular operation on the updated res, it only applies % on each individual dp[n][i]. 

    class Solution {
        public int dieSimulator(int n, int[] rollMax) {
            int mod = (int)1e9 + 7;
            long dp[][] = new long[n + 1][6];
            long res = 0;
    
            Arrays.fill(dp[1], 1);
    
            for(int i = 2; i <= n; i++) {
                for(int j = 0; j < 6; j++) {
                    for(int k = 0; k < 6; k++) {
                        if(j != k) {
                            dp[i][j] = (dp[i][j] + dp[i - 1][k])  % mod;
                        }
                        else {
                            int cnt = 2;
                            for(; cnt <= rollMax[j] && cnt < i; cnt++) {
                                for(int t = 0; t < 6; t++) {
                                    if(t == j) {
                                        continue;
                                    }
                                    dp[i][j] = (dp[i][j] + dp[i - cnt][t]) % mod;
                                }
                            }
                            if(cnt <= rollMax[j] && cnt == i) {
                                dp[i][j] = (dp[i][j] + 1) % mod;
                            }
                        }
                    }
                }
            }
            for(int i = 0; i < 6; i++) {
                res = (res + dp[n][i]) % mod;
            }
            return (int)res;
        }
    }

    Dynamic programming solution 2.

    State: Declare a 3D array: dp[2][6][16]. The 1st dimension is just a rolling index as we'll use previous computation result to calcuate the current iteration of 1 more dice roll. The 2nd dimension represents the last rolled number and the 3rd dimenstion represents the consecutive time of the last rolled number. Here the 3rd dimension is from 0 to 15 inclusive. 0 means that the last roll yields a different number, so the current number is not the last rolled number, thus its consecutive time is 0.

    Each time we have 1 more dice roll, we compute the new 2D array that represents the current state of all possible sequences counts. So the outer loop goes from 2 to n. Inside we do the following:

    1. reinitialize the new 2D array that will be computed.

    2. for each number i of consecutive time j, if the 1 more roll generates a number t that is not i,  dp[(k + 1) % 2][t][1] += dp[k % 2][i][j];   if t == i, then if we haven't exceeded the limit of rollMax[t], dp[(k + 1) % 2][t][j + 1] += dp[k % 2][t][j]. 

    Init: for the 1st roll, there is 1 sequence for each number of consecutive time 1.

    Answer:  All sequences must end at a number j from 1 to 6, with from 0 to 15 consecutive time of j. So the final answer is just the sum of last computed 2D array.

    class Solution {
        public int dieSimulator(int n, int[] rollMax) {
            int mod = (int)1e9 + 7;
            long res = 0;
            long[][][] dp = new long[2][6][16];
            
            //init: the 1st roll with a rolled number of consecutive time 1
            for(int i = 0; i < 6; i++) {
                dp[0][i][1] = 1;
            }
            
            //dp[(k + 1) % 2][i][j] has 1 more roll than dp[k % 2][i][j]
            for(int k = 2; k <= n; k++) {
                //re-init dp[(k + 1) % 2] before computing it
                for(int p = 0; p < 6; p++) {
                    Arrays.fill(dp[(k + 1) % 2][p], 0);
                }            
                for(int i = 0; i < 6; i++) {
                    for(int j = 0; j <= 15; j++) {
                        //the 1 more roll can be any number
                        for(int t = 0; t < 6; t++) {
                            if(t != i) {
                                dp[(k + 1) % 2][t][1] = (dp[(k + 1) % 2][t][1] + dp[k % 2][i][j]) % mod;
                            }
                            else if(j + 1 <= rollMax[t]) {
                                dp[(k + 1) % 2][t][j + 1] = (dp[(k + 1) % 2][t][j + 1] + dp[k % 2][t][j]) % mod;
                            }
                        }
                    }
                }
            }
            
            //sum up all sequences
            long[][] resDp = dp[(n + 1) % 2];
            for(int i = 0; i < 6; i++) {
                for(int j = 0; j <= 15; j++) {
                    res = (res + resDp[i][j]) % mod; 
                }
            }
            return (int)res;
        }
    }
  • 相关阅读:
    url 路径的拼接
    java 实现导出Excel文件
    window 使用频率最高的快捷键
    jeesite 框架的简单应用
    一个软件开发者的解决问题的心得——善于利用蛛丝马迹
    在linux上安装dotnetcore
    c#多线程同步之EventWaitHandle使用
    sharepoint 2013 安装
    模板模式的应用
    正则表达式的应用
  • 原文地址:https://www.cnblogs.com/lz87/p/11669196.html
Copyright © 2020-2023  润新知