--- title: 'APCS 2024/06 實作題題解 — C++' disqus: hackmd --- # APCS 2024/06 實作題題解 — C++ :::info :bulb: 此筆記為APCS 2024年06月實作題考試的題目詳解。每一題的題解都包含解題思路、C++範例程式碼。 ::: ## 第一題 特技表演 (ZeroJudge o076.) ### [題目](https://zerojudge.tw/ShowProblem?problemid=o076) :::success 有一個城鎮有 $n$ 棟高樓,樓高分別為 $h$~1~, $h$~2~, $h$~3~,市長想要在城鎮中心舉辦高空特技表演,該特技表演會從某棟大樓上朝右側滑翔至地面。為了表演人員的安全,滑翔的路徑樓高必須越來越低,請你找出一個最長的滑翔路徑。 ::: ### 輸入 / 輸出說明 | **輸入說明** | **輸出說明** | |:-:|:-:| | ![image](https://hackmd.io/_uploads/r1UYaVnBA.png) | ![image](https://hackmd.io/_uploads/SJkqTEnBC.png) | ### 解題思路 :::warning 這題只需要儲存前一樓高 lh 與 當前樓高 h,並依據以下規則執行: 1. 當前樓高 >= 前一樓高 => 不滿足條件,將路徑長度 t 設為 1 2. 當前樓高 < 前一樓高 => 將路徑長度 t + 1 若當前路徑長度 t > 最大路徑長度 max,則將 max 設為 t。 ::: ### 範例程式碼 ```C++= #include <iostream> using namespace std; int main () { ios::sync_with_stdio(false); cin.tie(0); int i; int n, max = 0, t = 1; cin >> n; int lh, h; cin >> lh; for (i=1;i<n;i++) { cin >> h; if (h >= lh) t = 1; else t++; if (t > max) max = t; lh = h; } cout << max; return 0; } ``` ### 運行結果 <font color="#00BB00">**AC**</font> (2ms, 344KB) ## 第二題 電子畫布 (ZeroJudge o077.) ### [題目](https://zerojudge.tw/ShowProblem?problemid=o077) :::success 有一個 $H × M$ 的電子畫布,一開始數值都是 $0$ 代表未填色,接下來請模擬 $N$ 次畫筆操作。 每次畫筆操作為選一個座標 $(r, c)$ 停留 $t$ 秒,他會將曼哈頓距離 $<= t$ 的區塊染上顏色 $x$。若有多個顏色重複填到相同區塊,顏色的數值會累加起來。 請輸出 $N$ 次操作後的畫布狀態。 ::: ### 輸入 / 輸出說明 | **輸入說明** | **輸出說明** | |:-:|:-:| | ![image](https://hackmd.io/_uploads/BkIKgS3rC.png) | ![image](https://hackmd.io/_uploads/Hkk9lBhHC.png) | ### 解題思路 :::warning 因為曼哈頓距離 <= t 的座標 (j, k) 一定滿足 $| j - r | <= t、| k - c | <= t$,因此我透過巢狀迴圈檢查 $r - t <= j <= r + t$ **、** $c - t <= k <= c + t$ 範圍內的座標是否滿足曼哈頓距離 $|j - r| + |k - c| <= t$,滿足條件則將位置的顏色染上 $x$,即 canva[j][k] = canva[j][k] + x。 ::: ### 範例程式碼 ```C++= #include <bits/stdc++.h> using namespace std; int main () { ios::sync_with_stdio(false); cin.tie(0); int i, j, k; int h, w, n; int r, c, t, x; cin >> h >> w >> n; int canva[h][w]={}; for (i=0;i<n;i++) { cin >> r >> c >> t >> x; for (j=r-t;j<=r+t;j++) for (k=c-t;k<=c+t;k++) if (j >= 0 && j < h && k >= 0 && k < w && abs(j-r) + abs(k-c) <= t) canva[j][k] = canva[j][k] + x; } for (i=0;i<h;i++) { for (j=0;j<w;j++) cout << canva[i][j] << " "; cout << endl; } return 0; } ``` ### 運行結果 <font color="#00BB00">**AC**</font> (3ms, 344KB) ## 第三題 缺字問題 (ZeroJudge o078.) ### [題目](https://zerojudge.tw/ShowProblem?problemid=o078) :::success 給定一個大小為 $K$ 個字母的集合和字串 $S$,求出在使用該集合所組出長度為 $L$ 字串中,不為字串 $S$ 子字串的最小字典序字串為何。 例如字母集合 **{a, c, m}**,其能組出長度為 $2$ 的字串字典序由小到大排序為 $aa < ac < am < ca < cc < cm < ma < mc < mm$。字串 $S$ 為 $accaamcm$,因此 $ma$ 為不在 $S$ 內的最小字典序字串。 ::: ### 輸入 / 輸出說明 | **輸入說明** | **輸出說明** | |:-:|:-:| | ![image](https://hackmd.io/_uploads/BJsvvHhSR.png) | ![image](https://hackmd.io/_uploads/S14uwHnrC.png) | ### 解題思路 :::warning 這個解法時間複雜度較高,請斟酌參考。 我這題使用遞迴的方法解。 透過範例測資#1為例,解釋遞迴邏輯: 1. 第一次遞迴選擇 'a' 加入暫存字串 $nc$ = *"a"*,此時 t = 0 2. 第二次遞迴選擇 'a' 加入暫存字串 $nc$ = *"aa"*,此時 t = 1 3. 第三次遞迴的 t = l = 2,返回第二次遞迴 4. 第二次遞迴選擇 'c' 加入暫存字串 $nc$ = *"ac"*,此時 t = 2 以此類推 t >= l 時(例如上述 3.)的字串為 $c$,在字串 $S$ 中尋找與 $c$ 相同的子字串,若無符合的子字串,輸出 $c$ 並結束遞迴,即可得解答(題目說輸入的字串 $K$ 由小到大排序,因此使用上述遞迴規則所得之暫存字串會由小到大)。 ::: ### 範例程式碼 ```C++= #include <bits/stdc++.h> using namespace std; string k, s; int l, sk; int stop = 0; int cw (int i, string c, int t) { int j; string nc; if (stop == 1) return 0; if (t >= l) { int ok = s.find(c); if (ok == -1) { cout << c; stop = 1; } return 0; } for (j=0;j<sk;j++) { nc = c + k[j]; cw(i+1, nc, t+1); } } int main () { int i; cin >> k >> l >> s; sk = k.size(); cw(i, "", 0); return 0; } ``` ### 運行結果 <font color="#00BB00">**AC**</font> (0.3s, 432KB) ###### tags: `APCS實作題` :::danger 查看更多資訊請至:https://www.tseng-school.com/ :::