โจทย์
ลูกเต๋า 6 หน้า ทอย n ครั้ง หน้า i ไม่อนุญาตให้ออกติดกันเกิน rollMax[i] ครั้ง (1-indexed) หาจำนวนลำดับที่เป็นไปได้
ตัวอย่าง: n=2, rollMax=[1,1,2,2,2,3] → 34 (จาก 36 ลบ (1,1) และ (2,2) ที่ห้ามติดกัน)
แนวคิด: DP + Memoization (Top-down)
State: dfs(dieLeft, lastFace, currLen) = จำนวนวิธีทอยอีก dieLeft ครั้ง โดยหน้าล่าสุดคือ lastFace ติดกันมา currLen ครั้ง
ขั้นตอน
- สร้าง 3D array
dp[n+1][6][16]ใส่ -1 (max rollMax = 15) - ฟังก์ชัน
dfs(dieLeft, last, currLen):- ถ้า
dieLeft == 0→ return 1 - ถ้า
dp[dieLeft][last][currLen] != -1→ return ค่าที่เก็บ ans = 0- ลูปหน้า
i = 0 ถึง 5:- ถ้า
i == last && currLen == rollMax[i]→ ข้าม (ห้ามติดต่อกันเกิน) nextLen = (i == last) ? currLen + 1 : 1ans = (ans + dfs(dieLeft-1, i, nextLen)) % MOD
- ถ้า
- เก็บและคืน
dp[dieLeft][last][currLen] = ans
- ถ้า
- เรียก
dfs(n, 0, 0)(last=0, currLen=0 เป็นค่าเริ่มต้น dummy)
ตัวอย่างโค้ด C++
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
class Solution {
int dfs(int dieLeft, int last, int currLen, vector<int>& rollMax,
vector<vector<vector<int>>>& dp) {
if (dieLeft == 0) return 1;
if (dp[dieLeft][last][currLen] != -1) return dp[dieLeft][last][currLen];
long long ans = 0;
for (int i = 0; i < 6; i++) {
if (i == last && currLen == rollMax[i]) continue;
int nextLen = (i == last) ? currLen + 1 : 1;
ans = (ans + dfs(dieLeft - 1, i, nextLen, rollMax, dp)) % MOD;
}
return dp[dieLeft][last][currLen] = ans;
}
public:
int dieSimulator(int n, vector<int>& rollMax) {
vector<vector<vector<int>>> dp(n+1, vector<vector<int>>(6, vector<int>(16, -1)));
return dfs(n, 0, 0, rollMax, dp);
}
};
int main() {
Solution ob;
vector<int> v = {1,1,2,2,2,3};
cout << ob.dieSimulator(2, v); // 34
return 0;
}
อินพุต / เอาต์พุต
Input: 2 [1,1,2,2,2,3]
Output: 34
ความซับซ้อน
- Time: O(n × 6 × 15 × 6) = O(n) - state ประมาณ n×6×15 แต่ละ state ลูป 6 รอบ
- Space: O(n × 6 × 16)