# Week 15 - 作業講解 Benson Chiu @ Sprout Taipei 2022 --- ## Problem #140 円円送禮物 ### 解題思路 - 觀察:第 $n$ 步的走法都可以從第 $n-1$ 步推得 - 限制條件: - 藍藍不相鄰,綠綠不相鄰 - 頭尾不能一藍一綠 ---- ### 也就是說 - 在推導第 $n$ 步的方法數時,我們不但要知道第 $n-1$ 步的方法數是多少 - 還要知道前一步的顏色(避免兩個顏色相鄰) - 也要知道來自第一步是某個顏色的方法數是多少(方便在最後的時候判斷頭尾不能一藍一綠的限制條件) --- ### 遞迴關係式 假設 $0$ 代表紅色,$1$ 代表綠色,$2$ 代表藍色。 $f(n,a,b)$代表:(1) 第 $n$ 格為顏色 $a$ 且 (2) 首格為顏色 $b$ 的方法數 #### 初始條件 $$ f(1,0,0) = f(1, 1, 1) = f(1,2,2) =1 $$ 其餘 $n = 1$ 的函數值皆為 $0$ #### 遞迴關係 ##### 第 $n$ 格為紅色 $$\begin{cases} f(n,0,0) = f(n-1,0,0) + f(n-1,1,0) + f(n-1,2,0) \\ f(n,0,1) = f(n-1,0,1) + f(n-1,1,1) + f(n-1,2,1) \\ f(n,0,2) = f(n-1,0,2) + f(n-1,1,2) + f(n-1,2,2)\end{cases}$$ ##### 第 $n$ 格為綠色 $$\begin{cases} f(n,1,0) = f(n-1,0,0) + f(n-1,1,0) \\ f(n,1,1) = f(n-1,0,1) + f(n-1,1,1) \\ f(n,1,2) = f(n-1,0,2) + f(n-1,1,2) \end{cases}$$ ##### 第 $n$ 格為藍色 $$\begin{cases} f(n,2,0) = f(n-1,0,0) + f(n-1,2,0) \\ f(n,2,1) = f(n-1,0,1) + f(n-1,2,1) \\ f(n,2,2) = f(n-1,0,2) + f(n-1,2,2) \end{cases}$$ ### 結束條件(假設所求為塗 $N$ 格的方法數) ==頭尾不能一藍一綠!!== $$f(N) = f(N,0,0) + f(N,0,1) + f(N,0,2) \text{ (Red)}\\ + f(N,1,0) + f(N,1,1) \text{ (Green)}\\+f(N,2,0) + f(N,2,2) \text{ (Blue)}$$ --- ### 想用遞迴嗎? - $N < 10^5$ ,可想而知遞迴樹有多龐大,準備吃 TLE 吧!:poop: - 解題小技巧:看到結果要除以 $MOD$ (此處為 $1000007$)的餘數,想到 DP ! - 在輸入測試資料前,我們就預先把表格建立好,之後每一次輸入詢問就直接存取! --- ### 建表 ```cpp= int dp[100001][3][3] = {0}; dp[1][0][0] = 1; dp[1][1][1] = 1; dp[1][2][2] = 1; for(int i = 2; i <= 1e5; i++) { dp[i][0][0] = (dp[i-1][0][0] + dp[i-1][1][0] + dp[i-1][2][0]) % MOD; dp[i][0][1] = (dp[i-1][0][1] + dp[i-1][1][1] + dp[i-1][2][1]) % MOD; dp[i][0][2] = (dp[i-1][0][2] + dp[i-1][1][2] + dp[i-1][2][2]) % MOD; dp[i][1][0] = (dp[i-1][0][0] + dp[i-1][1][0]) % MOD; dp[i][1][1] = (dp[i-1][0][1] + dp[i-1][1][1]) % MOD; dp[i][1][2] = (dp[i-1][0][2] + dp[i-1][1][2]) % MOD; dp[i][2][0] = (dp[i-1][0][0] + dp[i-1][2][0]) % MOD; dp[i][2][1] = (dp[i-1][0][1] + dp[i-1][2][1]) % MOD; dp[i][2][2] = (dp[i-1][0][2] + dp[i-1][2][2]) % MOD; } ``` --- ### 取值 ```cpp= int t = 0; cin >> t; for(int k = 0; k < t; k++) { int n; cin >> n; int ans = (dp[n][0][0] + dp[n][0][1] + dp[n][0][2] + dp[n][1][0] + dp[n][1][1] + dp[n][2][0] + dp[n][2][2]) % MOD; cout << ans << endl; } ``` --- ### 完整程式碼 ```cpp= #include<iostream> using namespace std; #define MOD (1000007) int main() { int dp[100001][3][3] = {0}; dp[1][0][0] = 1; dp[1][1][1] = 1; dp[1][2][2] = 1; for(int i = 2 i <= 1e5; i++) { dp[i][0][0] = (dp[i-1][0][0] + dp[i-1][1][0] + dp[i-1][2][0]) % MOD; dp[i][0][1] = (dp[i-1][0][1] + dp[i-1][1][1] + dp[i-1][2][1]) % MOD; dp[i][0][2] = (dp[i-1][0][2] + dp[i-1][1][2] + dp[i-1][2][2]) % MOD; dp[i][1][0] = (dp[i-1][0][0] + dp[i-1][1][0]) % MOD; dp[i][1][1] = (dp[i-1][0][1] + dp[i-1][1][1]) % MOD; dp[i][1][2] = (dp[i-1][0][2] + dp[i-1][1][2]) % MOD; dp[i][2][0] = (dp[i-1][0][0] + dp[i-1][2][0]) % MOD; dp[i][2][1] = (dp[i-1][0][1] + dp[i-1][2][1]) % MOD; dp[i][2][2] = (dp[i-1][0][2] + dp[i-1][2][2]) % MOD; } int t = 0; cin >> t; for(int k = 0; k < t; k++) { int n; cin >> n; int ans = (dp[n][0][0] + dp[n][0][1] + dp[n][0][2] + dp[n][1][0] + dp[n][1][1] + dp[n][2][0] + dp[n][2][2]) % MOD; cout << ans << endl; } } ``` --- ## Problem #1147 Syntax Checker ### 解題思路 - 字串從後往前遍歷,每一步更新目前的**句子數** - 分情形討論: - 遇到小寫:**句子數** + 1 - 遇到 $A$: - 若當前的**句子數** $<1$,表示無法組成一個句子,直接跳出迴圈輸出 "NO" - 否則,**句子數**不變 - 遇到 $B$: - 若當前的**句子數** $<2$,表示無法組成一個句子,直接跳出迴圈輸出 "NO" - 否則,**句子數** -1 - 遇到 $C$: - 若當前的**句子數** $<3$,表示無法組成一個句子,直接跳出迴圈輸出 "NO" - 否則,**句子數** -2 - 若迴圈有成功跑完,我們還需要判斷最終的**句子數**是否為 1 - 若是,輸出 "YES". - 若否,輸出 "NO". --- ### 完整程式碼 ```cpp= #include<iostream> #include<string> using namespace std; int main() { int t = 0; cin >> t; for(int i = 0; i < t; i++) { string s; cin >> s; int len = s.length(); int st = 0; bool res = true; if(!(s[0] <= 'z' && s[0] >= 'a' && len > 1)) { for(int j = len - 1; j >= 0; j--) { if(s[j] <= 'z' && s[j] >= 'a') st++; if(s[j] == 'A' && st < 1) { res = false; break; } if(s[j] == 'B') { if(st >= 2) st--; else { res = false; break; } } if(s[j] == 'C') { if(st >= 3) st -= 2; else { res = false; break; } } } } if(st == 1 && res) cout << "YES" << endl; else cout << "NO" << endl; } } ```