【遞推型 dp】B005_LC_擲骰子模擬(與本次數字相同或不相同)

一、Problem

有一個骰子模擬器會每次投擲的時候生成一個 1 到 6 的隨機數。

不過我們在使用它時有個約束,就是使得投擲骰子時,連續 擲出數字 i 的次數不能超過 rollMax[i](i 從 1 開始編號)。

現在,給你一個整數數組 rollMax 和一個整數 n,請你來計算擲 n 次骰子可得到的不同點數序列的數量。

假如兩個序列中至少存在一個元素不同,就認爲這兩個序列是不同的。由於答案可能很大,所以請返回 模 10^9 + 7 之後的結果。

輸入:n = 2, rollMax = [1,1,2,2,2,3]
輸出:34
解釋:我們擲 2 次骰子,如果沒有約束的話,共有 6 * 6 = 36 種可能的組合。
但是根據 rollMax 數組,數字 1 和 2 最多連續出現一次,所以不會出現序列 (1,1) 和 (2,2)。因

提示:

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

二、Solution

方法一:dp

  • 定義狀態
    • f[i][j][k]f[i][j][k] 表示第 ii 次扔出數字 jj 且數字 jj 連續出現 kk 次的方案數。
  • 思考初始化:
    • f[1][1...6][1]=1f[1][1...6][1] = 1
  • 思考狀態轉移方程
    • 如果 k>1k > 1f[i][j][k]=f[i1][j][k1]f[i][j][k] = f[i-1][j][k-1]
    • 如果 k=1k = 1f[i][j][k]=f[i][j][k] = 16f[i1][j][1...rollMax[j]]\sum_{1}^{6} f[i-1][j][1...rollMax[j]]
  • 思考輸出f[n][1...6][1...rollMaxj]f[n][1...6][1...rollMax_j]
class Solution {
    public int dieSimulator(int n, int[] rm) {
        int mod = (int) 1e9+7, f[][][] = new int[n+1][7+5][16];
        for (int j = 0; j < 6; j++) f[1][j][1] = 1;

        for (int i = 2; i <= n; i++)
        for (int j = 0; j < 6; j++)
        for (int k = 1; k <= rm[j]; k++) {
            if (k > 1) {
                f[i][j][k] = f[i-1][j][k-1];
            } else {
                int sum = 0;
                for (int jj = 0; jj < 6; jj++) if (jj != j) {
	                for (int kk = 1; kk <= rm[jj]; kk++) 
	                    sum = (sum + f[i-1][jj][kk]) % mod;
                }
                f[i][j][k] = sum;
            }
        }
        int ans = 0;
        for (int j = 0; j < 6; j++)
        for (int k = 1; k <= rm[j]; k++)
            ans = (ans + f[n][j][k]) % mod;
        
        return ans;
    }
}

複雜度分析

  • 時間複雜度:O(n×6×rollMaxi)O(n × 6 × rollMax_i)
  • 空間複雜度:O(n×6×rollMaxi)O(n × 6 × rollMax_i)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章