【Leetcode 1223.】 Dice Roll Simulation

很久没做leetcode了,这是一个动态规划的Hard题目

在算法导论里看到了下动态规划,跟很久以前初次看到的时候感受已经有很大的不同,已经很深刻地看到动态规划的结构了。于是顺利地把它做了出来。

题目

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

 解析

题意是投掷n次骰子。骰子是六面的,但是每个面能够连续投掷的次数有限,因此要注意。

首先假设已经有一个规模为k时的解决方案。

接着看此时k的结构是不是跟最终问题的结构相同。可以看出结构是相同的,但是要继续处理这个结构的细节。

细节就是“连续”,在处理“连续”这个含义的时候,以6个面为可选项,如果第k次投掷某一个面,则前面第k-1次的所有方案中可能有与这一次连续的也有不连续的。

因此再加一个变量区分这两种情况,即第三个分量为上一次连续的个数。

接着要注意初始化,这里的最小问题是投掷次数为1的时候,每个投掷方式的方案数是1。

然后看递归,按照 dp[i][j][k] = sum(dp[i - 1][j][k]); k = 1~max(rollMax[j]), j = 1~6进行递归求解。

所得方法即为下图。

class Solution {
public:
    int dieSimulator(int n, vector<int>& rollMax) {
        // suppose have rolled k times
        // the next time is k + 1
        // previous ...3, 3, [i] -> [i + 1], m = rollmax[3], i = (0, m)
        // dp[k][i][j] = sum(dp[k - 1][m][0-6])
        // i is conse number; j is last number; k is index
        // dp[n - 1][i][0-6]
        // vector<vector<vector<int>>> dp(n, vector<vector<int>>(7, vector<int>(16)));
        int dp[n][7][16];
        memset(dp, 0, sizeof(dp));
        // size is rollMax
        // add for dp[i][j][1] = dp[i][0-5][0]
        // initial
        for (int j = 1; j <= 6; j++) {
            dp[0][j][1] = 1;
        }
        int mod = 1e9 + 7;
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= 6; j++) {
                // the number should be less than rollMax
                // cur is j = 1...6
                for (int k = 2; k <= rollMax[j - 1] && k <= n; k++) {
                    // assert i >= 2
                    dp[i - 1][j][k] = (dp[i - 1][j][k] + dp[i - 2][j][k - 1]) % mod;
                }
                // previous is different to j, 1...5,
                for (int k = 1; k <= 6; k++) {
                    if (k == j) {
                        continue;
                    }
                    for (int l = 1; l <= rollMax[k - 1] && l <= i - 1; l++) {
                        // assert
                        dp[i - 1][j][1] = (dp[i - 1][j][1] + dp[i - 2][k][l]) % mod;
                    }
                }
            }
        }
        // sum dp[n][1-6][1-rollMax[1]]
        int res = 0;
        for (int j = 1; j <= 6; j++) {
            for (int k = 1; k <= rollMax[j - 1] && k <= n; k++) {
                res = (res + dp[n - 1][j][k]) % mod;
            }
        }
        return res;
    }
};

 

posted @ 2023-03-12 23:25  stackupdown  阅读(4)  评论(0编辑  收藏  举报