[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;
    }
}

 

posted @ 2019-10-14 01:42  Review->Improve  阅读(1399)  评论(0编辑  收藏  举报