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