<style> #doc pre { tab-size: 4; overflow: auto; } </style> {%hackmd @debbylin/theme-matcha %} # 113 進階程式設計個人筆記 期待最「優雅」的方式解決,盡可能兼具可讀性、簡潔、效能。(可讀性 >> 簡潔 > 效能) 若有任何建議或想法歡迎留言賜教。 ::: spoiler TODO - [x] 讓明天的自己能看懂 - [x] 使變數命名皆盡可能有意義 - [x] 良好 Coding Style - [x] Morden C++(no C-style array、pefer `static_cast`...) - [x] 清理編譯器警告(最高等級,含隱式符號轉換) - [ ] 補全思路與計算過程 ::: <!-- | 目錄 | | ----- | | [TOC] | --> <table> <thead> <tr> <th>目錄</th> </tr> </thead> <tbody> <tr> <td><ul> <li><a href="#1-基礎CC程式設計複習" title="1. 基礎C/C++程式設計複習">1. 基礎C/C++程式設計複習</a><ul> <li><a href="#a064-成績指標" title="a064: 成績指標">a064: 成績指標</a></li> <li><a href="#a096-時間差計算" title="a096: 時間差計算">a096: 時間差計算</a></li> <li><a href="#a104-雪花片片" title="a104: 雪花片片">a104: 雪花片片</a></li> </ul> </li> <li><a href="#2-函式-公用函式、自定函式" title="2. [函式] 公用函式、自定函式">2. [函式] 公用函式、自定函式</a><ul> <li><a href="#a114-找出最小的完全平方數" title="a114: 找出最小的完全平方數">a114: 找出最小的完全平方數</a></li> <li><a href="#a116-一起回家的日子" title="a116: 一起回家的日子">a116: 一起回家的日子</a></li> <li><a href="#a115-賓果遊戲" title="a115: 賓果遊戲">a115: 賓果遊戲</a></li> </ul> </li> <li><a href="#3-函式-遞迴函式" title="3. [函式] 遞迴函式">3. [函式] 遞迴函式</a><ul> <li><a href="#a157-費波那契數列" title="a157: 費波那契數列">a157: 費波那契數列</a></li> <li><a href="#a158-F91" title="a158: F91">a158: F91</a></li> <li><a href="#a288-執行路徑數" title="a288: 執行路徑數">a288: 執行路徑數</a></li> <li><a href="#a117-三色河內塔" title="a117: 三色河內塔">a117: 三色河內塔</a></li> <li><a href="#a118-2k與四個自然數平方和" title="a118: 2^k與四個自然數平方和">a118: 2^k與四個自然數平方和</a></li> </ul> </li> <li><a href="#4-基本資料型態-指標、多維陣列、串列" title="4. [基本資料型態] 指標、多維陣列、串列">4. [基本資料型態] 指標、多維陣列、串列</a><ul> <li><a href="#a147-促銷活動" title="a147: 促銷活動">a147: 促銷活動</a></li> <li><a href="#a159-錯誤更正" title="a159: 錯誤更正">a159: 錯誤更正</a></li> <li><a href="#a105-爺爺種樹" title="a105: 爺爺種樹">a105: 爺爺種樹</a></li> <li><a href="#a106-賓果遊戲" title="a106: 賓果遊戲">a106: 賓果遊戲</a></li> <li><a href="#a107-加密解密" title="a107: 加密解密">a107: 加密解密</a></li> <li><a href="#a287-文件解壓縮" title="a287: 文件解壓縮">a287: 文件解壓縮</a></li> </ul> </li> <li><a href="#5-基本資料型態-字串" title="5. [基本資料型態] 字串">5. [基本資料型態] 字串</a><ul> <li><a href="#a065-秘密差" title="a065: 秘密差">a065: 秘密差</a></li> <li><a href="#a067-ROT13" title="a067: ROT13">a067: ROT13</a></li> <li><a href="#a109-跑長編碼與資料壓縮" title="a109: 跑長編碼與資料壓縮">a109: 跑長編碼與資料壓縮</a></li> <li><a href="#a108-計算字串間隔距離" title="a108: 計算字串間隔距離">a108: 計算字串間隔距離</a></li> <li><a href="#a110-棒球遊戲" title="a110: 棒球遊戲">a110: 棒球遊戲</a></li> </ul> </li> <li><a href="#6-基本資料型態-結構" title="6. [基本資料型態] 結構">6. [基本資料型態] 結構</a><ul> <li><a href="#a150-多邊形面積" title="a150: 多邊形面積">a150: 多邊形面積</a></li> <li><a href="#a113-99遊戲" title="a113: 99遊戲">a113: 99遊戲</a></li> <li><a href="#a111-排隊" title="a111: 排隊">a111: 排隊</a></li> <li><a href="#a112-動物數量統計" title="a112: 動物數量統計">a112: 動物數量統計</a></li> </ul> </li> <li><a href="#7-基礎資料結構-I--堆疊、佇列" title="7. [基礎資料結構 I ] 堆疊、佇列">7. [基礎資料結構 I ] 堆疊、佇列</a><ul> <li><a href="#a151-後序式求值" title="a151: 後序式求值">a151: 後序式求值</a></li> <li><a href="#a120-中置式轉後置式" title="a120: 中置式轉後置式">a120: 中置式轉後置式</a></li> <li><a href="#a119-括號問題" title="a119: 括號問題">a119: 括號問題</a></li> <li><a href="#a276-撲克牌遊戲:手風琴" title="a276: 撲克牌遊戲:手風琴">a276: 撲克牌遊戲:手風琴</a></li> <li><a href="#a074-單線鐵路" title="a074: 單線鐵路">a074: 單線鐵路</a></li> <li><a href="#a163-印表機佇列" title="a163: 印表機佇列">a163: 印表機佇列</a></li> <li><a href="#a164-團體佇列" title="a164: 團體佇列">a164: 團體佇列</a></li> </ul> </li> <li><a href="#8-基礎資料結構-I--樹狀結構" title="8. [基礎資料結構 I ] 樹狀結構">8. [基礎資料結構 I ] 樹狀結構</a><ul> <li><a href="#a077-小球落下" title="a077: 小球落下">a077: 小球落下</a></li> <li><a href="#a076-二元搜尋樹高度" title="a076: 二元搜尋樹高度">a076: 二元搜尋樹高度</a></li> <li><a href="#a122-血緣關係" title="a122: 血緣關係">a122: 血緣關係</a></li> <li><a href="#a123-樹狀圖分析" title="a123: 樹狀圖分析">a123: 樹狀圖分析</a></li> </ul> </li> <li><a href="#9-基礎資料結構-I--圖形結構" title="9. [基礎資料結構 I ] 圖形結構">9. [基礎資料結構 I ] 圖形結構</a><ul> <li><a href="#a051-城市旅遊" title="a051: 城市旅遊">a051: 城市旅遊</a></li> <li><a href="#a102-油田" title="a102: 油田">a102: 油田</a></li> <li><a href="#a103-小群體" title="a103: 小群體">a103: 小群體</a></li> <li><a href="#a124-二分圖" title="a124: 二分圖">a124: 二分圖</a></li> <li><a href="#a125-觀光景點" title="a125: 觀光景點">a125: 觀光景點</a></li> <li><a href="#a126-馬步問題" title="a126: 馬步問題">a126: 馬步問題</a></li> </ul> </li> <li><a href="#10-基礎演算法-I--複雜度分析、排序" title="10. [基礎演算法 I ] 複雜度分析、排序">10. [基礎演算法 I ] 複雜度分析、排序</a><ul> <li><a href="#a127-連號或不連號" title="a127: 連號或不連號">a127: 連號或不連號</a></li> <li><a href="#a148-字元頻率" title="a148: 字元頻率">a148: 字元頻率</a></li> <li><a href="#a128-Agar.io" title="a128: Agar.io">a128: Agar.io</a></li> <li><a href="#a129-飛天桑妮" title="a129: 飛天桑妮">a129: 飛天桑妮</a></li> </ul> </li> <li><a href="#11-基礎演算法-I--排序與搜尋" title="11. [基礎演算法 I ] 排序與搜尋">11. [基礎演算法 I ] 排序與搜尋</a><ul> <li><a href="#a152-二分搜尋" title="a152: 二分搜尋">a152: 二分搜尋</a></li> <li><a href="#a153-二分法求解" title="a153: 二分法求解">a153: 二分法求解</a></li> <li><a href="#a290-完美購書計畫" title="a290: 完美購書計畫">a290: 完美購書計畫</a></li> <li><a href="#a130-人員調動" title="a130: 人員調動">a130: 人員調動</a></li> <li><a href="#a131-大黑馬" title="a131: 大黑馬">a131: 大黑馬</a></li> <li><a href="#a132-主機排程" title="a132: 主機排程">a132: 主機排程</a></li> </ul> </li> <li><a href="#12-基礎演算法-I--窮舉法" title="12. [基礎演算法 I ] 窮舉法">12. [基礎演算法 I ] 窮舉法</a><ul> <li><a href="#a088-最大乘積" title="a088: 最大乘積">a088: 最大乘積</a></li> <li><a href="#a133-採蘑菇攻略問題" title="a133: 採蘑菇攻略問題">a133: 採蘑菇攻略問題</a></li> <li><a href="#a154-除法" title="a154: 除法">a154: 除法</a></li> <li><a href="#a134-回文日期問題" title="a134: 回文日期問題">a134: 回文日期問題</a></li> <li><a href="#a135-巧克力擺盒" title="a135: 巧克力擺盒">a135: 巧克力擺盒</a></li> </ul> </li> <li><a href="#13-基礎資料結構-II-及基礎演算法-II--貪婪法" title="13. [基礎資料結構 II 及基礎演算法 II ] 貪婪法">13. [基礎資料結構 II 及基礎演算法 II ] 貪婪法</a><ul> <li><a href="#a091-加總的代價" title="a091: 加總的代價">a091: 加總的代價</a></li> <li><a href="#a071-排隊買飲料" title="a071: 排隊買飲料">a071: 排隊買飲料</a></li> <li><a href="#a141-基地台" title="a141: 基地台">a141: 基地台</a></li> <li><a href="#a139-背包置物問題" title="a139: 背包置物問題">a139: 背包置物問題</a></li> <li><a href="#a075-醜數" title="a075: 醜數">a075: 醜數</a></li> </ul> </li> <li><a href="#14-基礎資料結構-II-及基礎演算法-II--分而治之與回溯法" title="14. [基礎資料結構 II 及基礎演算法 II ] 分而治之與回溯法">14. [基礎資料結構 II 及基礎演算法 II ] 分而治之與回溯法</a><ul> <li><a href="#a165-最近點對問題" title="a165: 最近點對問題">a165: 最近點對問題</a></li> <li><a href="#a089-蘇丹王位繼承者" title="a089: 蘇丹王位繼承者">a089: 蘇丹王位繼承者</a></li> <li><a href="#a090-質數環" title="a090: 質數環">a090: 質數環</a></li> <li><a href="#a286-23" title="a286: 23">a286: 23</a></li> <li><a href="#a142-無刻度容器倒水問題" title="a142: 無刻度容器倒水問題">a142: 無刻度容器倒水問題</a></li> </ul> </li> <li><a href="#15-基礎資料結構-II-及基礎演算法-II--動態規劃" title="15. [基礎資料結構 II 及基礎演算法 II ] 動態規劃">15. [基礎資料結構 II 及基礎演算法 II ] 動態規劃</a><ul> <li><a href="#a098-計算方法數" title="a098: 計算方法數">a098: 計算方法數</a></li> <li><a href="#a155-雙子星塔" title="a155: 雙子星塔">a155: 雙子星塔</a></li> <li><a href="#a144-農作物採收問題" title="a144: 農作物採收問題">a144: 農作物採收問題</a></li> <li><a href="#a101-分銅幣" title="a101: 分銅幣">a101: 分銅幣</a></li> <li><a href="#a143-關鍵字搜尋模擬" title="a143: 關鍵字搜尋模擬">a143: 關鍵字搜尋模擬</a></li> <li><a href="#a145-搬家規畫問題" title="a145: 搬家規畫問題">a145: 搬家規畫問題</a></li> </ul> </li> <li><a href="#16-基礎資料結構-II-及基礎演算法-II--樹狀圖形結構演算法" title="16. [基礎資料結構 II 及基礎演算法 II ] 樹狀/圖形結構演算法">16. [基礎資料結構 II 及基礎演算法 II ] 樹狀/圖形結構演算法</a><ul> <li><a href="#a136-元件測試排程問題" title="a136: 元件測試排程問題">a136: 元件測試排程問題</a></li> <li><a href="#a137-勇者冒險" title="a137: 勇者冒險">a137: 勇者冒險</a></li> <li><a href="#a138-最小花費的航空之旅" title="a138: 最小花費的航空之旅">a138: 最小花費的航空之旅</a></li> </ul> </li> <li><a href="#17-題目解析-APCS-程式開發環境、題目解析" title="17. [題目解析] APCS 程式開發環境、題目解析">17. [題目解析] APCS 程式開發環境、題目解析</a><ul> <li><a href="#a160-線段覆蓋長度" title="a160: 線段覆蓋長度">a160: 線段覆蓋長度</a></li> <li><a href="#a140-物品堆疊" title="a140: 物品堆疊">a140: 物品堆疊</a></li> </ul> </li> </ul> </td> </tr> </tbody> </table> ## 1. 基礎C/C++程式設計複習 ### a096: 時間差計算 這題仔細看範例,可以知道是要後減前,再以一天為除數[取模](https://zh.wikipedia.org/zh-tw/模除)。不過很遺憾,C++ 中 `%` 運算子是求餘,結果正負跟隨被除數,所以多加了一個 86400 避免負結果。 ```cpp= #include <iomanip> #include <iostream> #include <string> int main () { std::string str; int n = 86400; std::cin >> str; n -= std::stoi(str.substr(0, 2)) * 3600 + std::stoi(str.substr(3, 2)) * 60 + std::stoi(str.substr(6, 2)); std::cin >> str; n += std::stoi(str.substr(0, 2)) * 3600 + std::stoi(str.substr(3, 2)) * 60 + std::stoi(str.substr(6, 2)); n %= 86400; std::cout << std::setfill('0') << std::setw(2) << n / 3600 << ':' << std::setw(2) << n % 3600 / 60 << ':' << std::setw(2) << n % 60 << std::endl; return 0; } ``` ### a064: 成績指標 沒什麼好說的。 ```cpp= #include <algorithm> #include <iostream> #include <vector> int main () { int n {}; std::cin >> n; std::vector<int> scores(n); int wc {101}; int bc {-1}; for (auto& score : scores) { std::cin >> score; if (score >= 60) { if (score < wc) wc = score; } else if (score > bc) bc = score; } std::sort(scores.begin(), scores.end()); for (const auto& score : scores) std::cout << score << ' '; std::cout << "\n"; if (bc == -1) std::cout << "best case\n"; else std::cout << bc << "\n"; if (wc == 101) std::cout << "worst case\n"; else std::cout << wc << "\n"; return 0; } ``` ### a104: 雪花片片 首先使用瞪眼法,易發現三角形數是 4 的次方,解決。(大誤 設第 $n$ 個圖形有 $f(n)$ 個邊,$g(n)$ 個三角形。不難得出: $$ \begin{align} f(1)&=3\\ f(n)&=f(n-1)\times4\\ g(1)&=1\\ g(n)&=g(n-1)+f(n-1) \end{align} $$ 透過級數公式和數學歸納法可得: $$ \begin{align} f(n) &= 3 \times 4^{n-1}\\ g(n) &= 1 + 3(4^0+4^1+4^2+\dots+4^{n-3}+4^{n-2})\\ &= 1 + 3 \times \frac{4^{n-1}-1}{4-1}\\ &= 1 + 4^{n-1} - 1\\ &= 4^{n-1} \end{align} $$ 設累積三角形數(即題目要求的答案)為 $h(n)$。 $$ \begin{align} h(n) &= g(1) + g(2) + \dots + g(n-1) + g(n)\\ &= 1 + 4 + 16 + \dots + 4^{n-2} + 4^{n-1}\\ &= \frac{4^n-1}{4-1}\\ &= \frac{4^n-1}3 \end{align} $$ 好,雖然推導複雜了點,但蠻簡單的嘛?程式就一段 `(pow(4,n)+1)/3`,怎麼會說是最難的呢? > 測試資料只有一行,只有一個數字 N,其值為 1 至 <u>120</u> 的整數。 $$ \begin{align} h(120) &= 1 + 4 + 16 + \dots + 4^{118} + 4^{119}\\ &> 4^{119} = 2^{238}\\ &\gg 2^{64} \end{align} $$ > `uint64_t` 的取值為 0 ~ 2^64^-1。 爆炸!由於 C/C++ 沒有內建大整數(是的 Python、Java都有),那就只能手搓大數運算了。也即儲存每一數位,自己做直式運算。 所以我們要做 N 次乘法(迭代$4^n$)、一個減法($-1$)、再一個除法($\frac{}3$)直式運算?不,這不優雅。仔細觀察: $$ \begin{align} h(n) &= 1 + 4 + 16 + \dots + 4^{n-3} + 4^{n-2} + 4^{n-1} \\ &= 1 + 4\left(1 + 4 + 16 + \dots + 4^{n-3} + 4^{n-2}\right) \\ &= 1 + 4h(n-1) \\ h(1) &= 1 \end{align} $$ $x \leftarrow 4x+1$ <ruby>遞迴<rp>(</rp><rt style="font-size:0.6em;">疊代</rt><rp>)</rp></ruby>,漂亮! ```cpp= #include <iostream> #include <vector> int main () { // 倒序存(最高位在最後)比較好計算,故最後輸出時要反過來。 std::vector<int> bigNum {0}; int n {}; std::cin >> n; while (n--) { int c {1}; for (auto& x : bigNum) { const int temp {x * 4 + c}; c = temp / 10; x = temp % 10; } if (c) bigNum.push_back(c); } for (auto i {bigNum.rbegin()}; i != bigNum.rend(); i++) std::cout << *i; std::cout << '\n'; return 0; } ``` 另外其實裡面的 10 可以換成大一點的數字以大幅提升計算效率(只要是十的次方並確保不超過上限),但那樣還要用 `std::setprecision()` 確保 0 不會被吃掉,而我懶就沒弄了。 ## 2. [函式] 公用函式、自定函式 ### a114: 找出最小的完全平方數 一個一格數字確認是否是符合條件的數肯定會爆的,那該如何最佳化呢? * 由底數找平方數而非反過來檢查 * 跳過奇數,因其平方必亦為奇數 * 若且唯若 $\sqrt{10^{n+1}} \le x < \sqrt{10^{n+2}}$,$x^2$ 的位數為 $n$ * 平方差 $(n+2)^2-n^2 = (2)(2n+2) = 4n+4$ * $\sqrt{10^{2k}} = 10^k$,$\sqrt{10^{2k+1}} = 10^k\sqrt{10}$ * ~~這題才 10 種可能,最好的解法應該是硬編碼才對 XD~~ - 但還是要先寫個程式算過就是(`constexpr`:叫我?) ```cpp= #include <iostream> bool all_even (int n, int length) { while (length--) { if (n % 2) break; n /= 10; } return !n; } int main () { constexpr double sqrt10 {3.1622776602}; int n {}; std::cin >> n; while (n--) { int length {}; std::cin >> length; int base {1}; for (int i = 0; i < (length - 1) / 2; i++) base *= 10; if (length % 2 == 0) base *= sqrt10; base += base % 2; long long square {base * base}; while (!all_even(square, length)) { square += 4 * base + 4; base += 2; } std::cout << square << '\n'; } return 0; } ``` ### a116: 一起回家的日子 > <ruby>不要重新發明輪子<rt>Don't reinvent the wheel</rt></ruby> 這一章叫做公用函式,而傳說有種東西叫[標準庫](https://en.cppreference.com)。 不得不吐槽一下,Python 時就知道要引庫,怎麼變成 C++ 就都不會了。 ```cpp= #include <ctime> #include <iomanip> #include <iostream> int gcd (int a, int b) { return b == 0 ? a : gcd(b, a % b); } int main () { int timelong {1}; int n {}; std::cin >> n; while (n--) { int in {}; std::cin >> in; timelong *= in / gcd(in, timelong); } std::tm date {}; std::cin >> std::get_time(&date, "%Y/%m/%d"); date.tm_mday += timelong; std::mktime(&date); std::cout << std::put_time(&date, "%Y/%m/%d") << "\n"; return 0; } ``` 要不是 [`std::lcm`](https://en.cppreference.com/w/cpp/numeric/lcm) C\++17 才加入,而我們系統很爛的只支援 C\++14,還可以寫得更漂亮。 ### a115: 賓果遊戲 記錄每行列斜的狀態及每個數字所在的座標,最後比對每數字所在之處,有多少臨門一腳的行列斜。 然後我不加 `#pragma` 它不會幫我 `inline`。 ```cpp= #pragma GCC optimize("O3") #include <iostream> #include <vector> template <typename T> constexpr std::size_t u(T i) { return static_cast<std::size_t>(i); } int main () { std::vector<int> x_state(5), y_state(5); int slash_state {0}, bslash_state {0}; std::vector<int> x_pos(25), y_pos(25); bool state[25] = {}; for (int y = 0; y < 5; y++) { for (int x = 0; x < 5; x++) { int number {}; std::cin >> number; number--; x_pos[u(number)] = x; y_pos[u(number)] = y; } } int number {}; std::cin >> number; while (number != -1) { number--; x_state[u(x_pos[u(number)])]++; y_state[u(y_pos[u(number)])]++; if (x_pos[u(number)] == y_pos[u(number)]) bslash_state++; if (x_pos[u(number)] + y_pos[u(number)] == 4) slash_state++; state[number] = true; std::cin >> number; } int max_score {}; int answer {}; for (int i = 24; i >= 0; i--) { if (state[i]) continue; int score { (x_state[u(x_pos[u(i)])] == 4) + (y_state[u(y_pos[u(i)])] == 4) + (x_pos[u(i)] == y_pos[u(i)] && bslash_state == 4) + (x_pos[u(i)] + y_pos[u(i)] == 4 && slash_state == 4) }; if (score >= max_score) { max_score = score; answer = i; } } std::cout << answer + 1 << std::endl; return 0; } ``` ## 3. [函式] 遞迴函式 ### a157: 費波那契數列 題目叫你用遞迴就用遞迴。 遞迴爆炸?[尾遞迴](https://zh.wikipedia.org/zh-tw/尾调用)是種好東西。 ```cpp= #include <iostream> int f (int n, int f_nm1 = 1, int f_nm2 = 0) { return n == 0 ? f_nm2 : f(n - 1, f_nm1 + f_nm2, f_nm1); } int main () { int n {}; while (std::cin >> n) std::cout << f(n) << std::endl; return 0; } ``` ### a158: F91 瞪眼法:名字裡有 91 答案自然是 91。(誤&times;2) 設 $90 \le n \le 100$,歸納法得證: $$ \begin{align} F91(100) &= F91(F91(111))\\ &= F91(101)\\ &= 91\\ F91(n) &=F91(F91(n+11))\\ &=F91(n + 11 - 10)\\ &=F91(n+1)\\ &= 91\\ \end{align} $$ 設 $n \le 89$,假設 $F91(n) = 91$ 歸納法得證: $$ \begin{align} F91(89) &= F91(F91(100))\\ &= F91(91)\\ &= 91\\ F91(n) &=F91(F91(n+11))\\ &=F91(91)\\ &= 91\\ \end{align} $$ 可得: $$ F91(n) = \left\{ \begin{array}{cl} 91 & : \ n \le 100 \\ n-10 & : \ n \ge 101 \end{array} \right. $$ ```cpp= #include <iostream> int f91 (int n) { return n <= 100 ? 91 : n - 10; } int main () { int n; std::cin >> n; while (n != 0) { std::cout << "f91(" << n << ") = " << f91(n) << "\n"; std::cin >> n; } return 0; } ``` ### a288: 執行路徑數 將輸入的所有 `S` 刪去,`IF` 換成 `(1`,`ELSE` 換成 `+1`,`END_IF` 換成 `)`,就能得出答案的算式。例如: ``` IF ELSE IF ELSE END_IF END_IF IF IF ELSE END_IF IF ELSE END_IF ELSE IF ELSE END_IF END_IF ``` 以上輸入的答案即為 `(1+1(1+1))(1(1+1)(1+1)+1(1+1))`,原理學排列組合時已講過,此不再贅述。 再觀察以下計算過程: ``` (1+1(1+1))(1(1+1)(1+1)+1(1+1)) (1+1(2))(1(1+1)(1+1)+1(1+1)) (1+2)(1(1+1)(1+1)+1(1+1)) 3(1(1+1)(1+1)+1(1+1)) 3(1(2)(1+1)+1(1+1)) 3(2(1+1)+1(1+1)) 3(2(2)+1(1+1)) 3(4+1(1+1)) 3(4+1(2)) 3(4+2) 3(6) 18 ``` 轉換成程式碼就是: ```cpp= #include <iostream> #include <stack> #include <string> #include <utility> int main () { int n {}; std::cin >> n; while (n--) { std::stack<std::pair<long long, long long>> stack {}; stack.push({1, 0}); while (true) { std::string keyword {}; std::cin >> keyword; if (keyword == "S") { continue; } else if (keyword == "IF") { stack.push({1, 0}); } else if (keyword == "ELSE") { stack.top().second = stack.top().first; stack.top().first = 1; } else if (keyword == "END_IF") { const auto& temp {stack.top()}; stack.pop(); stack.top().first *= temp.first + temp.second; } else if (keyword == "ENDPROGRAM") { break; } } std::cout << stack.top().first << '\n'; } return 0; } ``` 雖然本題叫遞迴,但遞迴時語句歸屬可能有些燒腦,這邊採用較易理解的堆疊。 另外該寫法還有個好處:雖原題目必有且僅有一個 `ELSE`,只要為本程式碼加一個符號就能使其接受任意多的 `ELSE`……。 :::spoiler 任意多 `ELSE` 第 20 行`=`改為`+=`: ```cpp=20 stack.top().second += stack.top().first; ``` ::: ### a117: 三色河內塔 畫圖解,我懶得把圖放上來了。 由於自始移動都是拿最上面的 n 個環,可以直接數出環的編號。故實際上不需存儲塔的狀態,徑輸出即可。 ```cpp= #include <iostream> int moveTopOneRing (char from, char to, int id){ std::cout << "ring " << id << " : " << from << " => " << to << "\n"; return 1; } int moveTopNRings (int n, char from, char to, char through) { if (n == 1) return moveTopOneRing(from, to, n); int steps {0}; steps += moveTopNRings(n - 1, from, through, to); steps += moveTopOneRing(from, to, n); steps += moveTopNRings(n - 1, through, to, from); return steps; } int distributeTopNRings (int n, char from, char a, char b) { if (n == 0) return 0; if (n == 1) return moveTopOneRing(from, b, n); int steps {0}; steps += moveTopNRings(n - 1, from, a, b); steps += moveTopOneRing(from, b, n); steps += distributeTopNRings(n - 2, a, b, from); return steps; } int main () { int n {}; std::cin >> n; const int steps{ distributeTopNRings(n, 'A', 'B', 'C') }; std::cout << "共需" << steps << "個移動\n"; return 0; } ``` ### a118: 2^k與四個自然數平方和 > 想到「若」很簡單,但欲證明「唯若」就要費很大一番功夫了。 假設 $a_1,a_2,a_3,a_4$ 皆為奇數,設 $a_i=2b_i+1$,則: <!-- &=4b_1^2+4b_1+1+4b_2^2+4b_2+1+4b_3^2+4b_3+1+4b_4^2+4b_4+1\\--> $$ \begin{align} 2^k &=(2b_1+1)^2+(2b_2+1)^2+(2b_3+1)^2+(2b_4+1)^2\\ &=4(b_1^2+b_1+b_2^2+b_2+b_3^2+b_3+b_4^2+b_4 + 1)\\ 2^{k-2} &=b_1^2+b_1+b_2^2+b_2+b_3^2+b_3+b_4^2+b_4 + 1\\ \end{align} $$ 對於任何自然數 $x$,$x\equiv x^2 \pmod2$,可知 $x+x^2 \equiv 0\pmod2$。 $$ \displaylines{ 2^{k-2} \equiv 0 \pmod2 \\ \begin{align} 2^{k-2} &\equiv (b_1^2+b_1)+(b_2^2+b_2)+(b_3^2+b_3)+(b_4^2+b_4) + 1\\ &\equiv 0 + 0 + 0 + 0 +1\\ &\equiv 1\\ 0 &\equiv 1 \Rightarrow\!\Leftarrow\\ \end{align}\pmod2 } $$ 矛盾,故當 $k\ge3$ 時 $a_1,a_2,a_3,a_4$ 不可能皆為奇數。 再假設 $a_1,a_2,a_3,a_4$ 中共兩個奇數,不失一般性的令 $a_1=2b_1+1$、$a_2=2b_2+1$、$a_3=2b_3$、$a_4=2b_4$,則: $$ \begin{align} 2^k&=(2b_1+1)^2+(2b_2+1)^2+(2b_3)^2+(2b_4)^2\\ &=4(b_1^2+b_1+b_2^2+b_2+b_3^2+b_4^2)+2 \end{align} $$ 觀察到: $$ \displaylines{ 2^k \equiv 4\times2^{k-2} \equiv 0 \pmod4 \\ \begin{align} 2^k &\equiv 4(b_1^2+b_1+b_2^2+b_2+b_3^2+b_4^2)+2\\ &\equiv 0 + 2\\ &\equiv 2\\ 0 &\equiv 2 \Rightarrow\!\Leftarrow\\ \end{align}\pmod4 } $$ 矛盾,故當 $k\ge2$ 時 $a_1,a_2,a_3,a_4$ 不可能共兩個奇數。 一個奇數及三個奇數的情況不言自明:奇數平方亦為奇數,奇數個奇數相加結果必為奇數。然 $2^k$ 為偶數,產生矛盾,不可能。 故給定 $k\ge3$,$a_1,a_2,a_3,a_4$ 必皆為偶數,設 $a_i=2b_i$: $$ \begin{align} 2^k &=a_1^2 + a_2^2 + a_3^2 + a_4^2\\ &=(2b_1)^2+(2b_2)^2+(2b_3)^2+(2b_4)^2\\ &=4(b_1^2+b_2^2+b_3^2+2b_4^2)\\ 2^{k-2} &= b_1^2+b_2^2+b_3^2+b_4^2\\ \end{align} $$ 不難看出,對於任意 $k\ge3$,$2^k$ 的所有解 $a_1,a_2,a_3,a_4$ 必定與 $2^{k-2}$ 的所有解 $\frac{a_1}{2},\frac{a_2}{2},\frac{a_3}{2},\frac{a_4}{2}$ 一一對應(對射)。又若 $k-2 \geq 3$,依然可確定它們仍皆為偶數,並同理進一步往下對應,直至所有解都與 $2^2$ 的或 $2^1$ 的解以差了二的冪倍之方式對應。 簡單觀察試算可得知:$2^1$ 時無解,$2^2$ 僅有唯一解 $2^2=1^2+1^2+1^2+1^2$。以上述解的一一對應性推而廣之,綜上可以證明: $$ \displaylines{ 2^k = a_1^2 + a_2^2 + a_3^2 + a_4^2\\ \iff \\ (k = 2n) \land (a_1 = a_2 = a_3 = a_4 = 2^{n-1}) } $$ C++ 寫出來就是: ```cpp= #include <iostream> int main () { int k {}; std::cin >> k; if (k % 2) { std::cout << "0\n"; return 0; } const int ans{ 1 << (k / 2 - 1) }; std::cout << ans << ' ' << ans << ' ' << ans << ' ' << ans << '\n'; return 0; } ``` ## 4. [基本資料型態] 指標、多維陣列、串列 ### a147: 促銷活動 題目要求填空,風格突變請見諒。 ```cpp= #include <iostream> using namespace std; void discount(double& p1, double& p2){ if (p1 == p2) p2 /= 2; } int main(){ double p1, p2; cout << "Original price:" << endl; cin >> p1 >> p2; discount(p1, p2); cout << "Price after discount:" << endl; cout << p1 << " " << p2 << endl; return 0; } ``` ### a159: 錯誤更正 關鍵字「奇偶校驗」。另外奇偶結果等於互斥或(XOR)結果,不必真的去加。 ```cpp= #include <bitset> #include <iostream> int main () { int n {}; std::cin >> n; while (n != 0) { int flippedColumn {0}; int flippedRow {0}; std::bitset<100> columnSum {}; for (int i = 0; i < n; i++) { bool rowSum {false}; for (std::size_t j = 0; j < n; j++) { bool bit {}; std::cin >> bit; rowSum ^= bit; if (bit) columnSum.flip(j); } if (rowSum) flippedRow = flippedRow ? -1 : i + 1; } for (int j = 0; j < n; j++) if (columnSum[static_cast<std::size_t>(j)]) { flippedColumn = flippedColumn ? -1 : j + 1; } if (flippedRow > 0 && flippedColumn > 0) { std::cout << "Change bit (" << flippedRow << "," << flippedColumn << ")\n"; } else if (flippedRow == 0 && flippedColumn == 0) { std::cout << "OK\n"; } else { std::cout << "Corrupt\n"; } std::cin >> n; } return 0; } ``` ### a105: 爺爺種樹 ~~目前該寫法尚未整理,請勿模仿!~~ 重寫完成。 由於 `std::vector<bool>` 的缺陷,這裡以另一種方式儲存。 特別感謝孟學老師提供關於利用定義步進來合併不同情況的思路。 ```cpp= #include <iostream> #include <set> #include <utility> constexpr int sign (int x) { return (x > 0) - (x < 0); } int main () { int x_length {}, y_length {}; std::cin >> x_length >> y_length; std::set<std::pair<int, int>> map {}; int lines {}; std::cin >> lines; while (lines--) { int start_x {}, start_y {}, end_x {}, end_y {}; std::cin >> start_x >> start_y >> end_x >> end_y; const int dx {sign(end_x - start_x)}; const int dy {sign(end_y - start_y)}; for (int x {start_x}, y {start_y}; ; x += dx, y += dy) { map.emplace(x, y); if (x == end_x && y == end_y) break; } } std::cout << map.size() << '\n'; return 0; } ``` ### a106: 賓果遊戲 很多時候會發現硬寫的優雅之處。真去寫四個迴圈不比直接邏輯式還長還醜? ~~另,可以用結構把 `names` 與 `disks` 結合,但我懶得改了。~~ 改好了。 ```cpp= #include <iostream> #include <utility> #include <vector> bool is_bingo (const std::vector<int>& disk) { return ( !(disk[0] || disk[1] || disk[2] || disk[3] ) || !(disk[4] || disk[5] || disk[6] || disk[7] ) || !(disk[8] || disk[9] || disk[10] || disk[11]) || !(disk[12] || disk[13] || disk[14] || disk[15]) || !(disk[0] || disk[4] || disk[8] || disk[12]) || !(disk[1] || disk[5] || disk[9] || disk[13]) || !(disk[2] || disk[6] || disk[10] || disk[14]) || !(disk[3] || disk[7] || disk[11] || disk[15]) || !(disk[0] || disk[5] || disk[10] || disk[15]) || !(disk[3] || disk[6] || disk[9] || disk[12]) ); } int main () { char symbol {}; std::size_t player_num {}; std::cin >> symbol >> player_num; // std::pair<name, disk> std::vector<std::pair<char, std::vector<int>>> players(player_num, {{}, std::vector<int>(16)}); for (auto& player : players) { std::cin >> player.first; for (auto& n : player.second) std::cin >> n; } std::cin >> symbol; std::vector<char> bingo {}; for (int i {0}; i < 16; i++) { int said_number {}; std::cin >> said_number; std::cout << said_number << ' '; for (auto& player : players) { for (auto& number : player.second) if (number == said_number) number = 0; if (is_bingo(player.second)) bingo.push_back(player.first); } if (!bingo.empty()) break; } for (const auto& name : bingo) std::cout << name << ' '; std::cout << '\n'; return 0; } ``` ### a107: 加密解密 二維陣列平坦化。 ~~目前這個解為了避免隱式符號轉換增加了些複雜。~~ 重寫完成。 ```cpp= #include <cctype> #include <string> #include <iostream> int main() { const std::string tableLower { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; const std::string tableUpper { 'E', 'X', 'A', 'M', 'P', 'L', 'B', 'C', 'D', 'F', 'G', 'H', 'I', 'J', 'K', 'N', 'O', 'R', 'S', 'T', 'U', 'V', 'W', 'Y', 'Z' }; std::size_t length {}; std::cin >> length; std::string input(length, '0'); for (auto i {input.rbegin()}; i != input.rend(); i++) std::cin >> *i; const bool is_upper {std::isupper(input[0]) != 0}; const auto decode {is_upper ? tableUpper : tableLower}; const auto encode {is_upper ? tableLower : tableUpper}; for (std::size_t i = 0; i < length; i += 2) { const auto a_pos {decode.find(input[i])}; const auto b_pos {decode.find(input[i + 1])}; std::cout << encode[a_pos / 5 * 5 + b_pos % 5] << encode[b_pos / 5 * 5 + a_pos % 5]; } return 0; } ``` ### a287: 文件解壓縮 題目怎麼說就怎麼做,不必多言。 ```cpp= #include <cctype> #include <iostream> #include <string> #include <vector> int main() { std::string reading {}; std::string input_line {}; std::vector<std::string> words {}; bool reading_word {false}; bool reading_number {false}; while (std::getline(std::cin, input_line)) { if (input_line == "0") return 0; input_line.push_back('\n'); for (const auto& c : input_line) { if (std::isalpha(c) || std::isdigit(c)) { reading.push_back(c); reading_word = std::isalpha(c); reading_number = std::isdigit(c); if (reading_number) continue; } else if (reading_word) { words.push_back(reading); reading.clear(); reading_word = false; } else if (reading_number) { const auto word {std::prev(words.end(), std::stoi(reading))}; std::cout << *word; words.push_back(*word); words.erase(word); reading.clear(); reading_number = false; } std::cout << c; } } return 0; } ``` ## 5. [基本資料型態] 字串 ### a065: 秘密差 透過不斷翻轉符號來達成一加一減,就無需另用一個變數做判斷。 冷知識:`c - '0'` 結果的正確有 C++ 標準保證。 ```cpp= #include <string> #include <iostream> int main () { std::string number {}; std::cin >> number; int secrect_nmber {}; for (const auto& c : number) secrect_nmber = -secrect_nmber + c - '0'; if (secrect_nmber < 0) secrect_nmber *= -1; std::cout << secrect_nmber << '\n'; return 0; } ``` ### a067: ROT13 `std::noskipws` 旗標能避免跳過空白。 都做 ASCII 加減了,ASCII 比大小就不用避諱了。 ```cpp= #include <iostream> int main() { std::cin >> std::noskipws; char c {}; while (std::cin >> c) { if ('a' <= c && c <= 'z') std::cout << static_cast<char>((c - 'a' + 13) % 26 + 'a'); else if ('A' <= c && c <= 'Z') std::cout << static_cast<char>((c - 'A' + 13) % 26 + 'A'); else std::cout << c; }; return 0; } ``` ### a109: 跑長編碼與資料壓縮 注意本題有個大坑:若沒有 `>> std::ws`,`>> n` 吃掉數字後不會吃掉第一行的換行符,將導致迴圈第一次讀入空行(失去了數字的第一行)。有另一種解法是開頭迴圈外多叫一次 `std::getline` 吃掉第一行,或是使用 `std::cin.ignore()` 單獨吃掉數字後的換行符。不過 `std::ws` 本身有讓 debug 比較方便的好處。 `std::bitset<n>(x)`是個輸出二進位字串的小技巧。 ```cpp= #include <bitset> #include <cmath> #include <iostream> #include <sstream> #include <string> enum State { reading_0 = '0', reading_1 = '1', not_reading, error }; int main() { int n {}; std::cin >> n; while (n--) { std::string line {}; std::getline(std::cin >> std::ws, line); std::stringstream sstream {}; unsigned int length {1}; double size {1}; State state {not_reading}; for (const auto& c : line) { if (state == c && length < 7) length++; else if (state != not_reading) { sstream << static_cast<char>(state) << std::bitset<3>(length) << ' '; length = 1; size++; } if (c == '0') state = reading_0; else if (c == '1') state = reading_1; else { std::cout << "-1\n"; state = error; break; } } if (state == error) continue; sstream << static_cast<char>(state) << std::bitset<3>(length) << ' ' << std::round(size * 400. / line.size()) << "%\n"; std::cout << sstream.rdbuf(); } return 0; } ``` ### a108: 計算字串間隔距離 * 統一大小寫 = 忽略大小寫 * 善用布林標誌 ```cpp= #include <cctype> #include <iostream> #include <string> int main() { std::string str {}; std::cin >> str; char find {}; std::cin >> find; for (auto& c : str) c = static_cast<char>(std::toupper(c)); find = static_cast<char>(std::toupper(find)); std::size_t start {0}; bool first {true}; while (true) { const auto pos {str.find(find, start)}; if (pos == str.npos) break; if (!first) std::cout << pos - start + 1 << " "; else first = false; start = pos + 1; } std::cout << "\n"; return 0; } ``` ### a110: 棒球遊戲 * 平坦化打擊至有序陣列 * 利用增長的陣列紀錄選手的跑壘紀錄 ```cpp= #include <bitset> #include <iostream> #include <string> #include <vector> enum class Hit { B1, B2, B3, B4, OUT, UNKNOWN }; Hit prase_hit_name (const std::string& name) { if (name == "1B") return Hit::B1; else if (name == "2B") return Hit::B2; else if (name == "3B") return Hit::B3; else if (name == "HR") return Hit::B4; else if (name == "FO" || name == "GO" || name == "SO") return Hit::OUT; else return Hit::UNKNOWN; } int main() { constexpr int player_number {9}; constexpr std::size_t max_step {27}; std::vector<Hit> hits(max_step, Hit::UNKNOWN); for (int i {0}; i < player_number; i++) { int hit_times {}; std::cin >> hit_times; for (int j {0}; j < hit_times; j++) { std::string hit_name {}; std::cin >> hit_name; const std::size_t index {static_cast<std::size_t>(player_number * j + i)}; if (index >= max_step) continue; hits[index] = prase_hit_name(hit_name); } } int target_num {}; std::cin >> target_num; std::bitset<max_step * 4> player {}; std::size_t move_times {0}; int out_times {0}; for (std::size_t i = 0; out_times < target_num; i++) { if (hits[i] == Hit::OUT) { out_times++; if (out_times % 3) continue; if (move_times <= 3) player.reset(); else { player.reset(move_times - 1); player.reset(move_times - 2); player.reset(move_times - 3); } continue; } player.set(move_times); switch (hits[i]) { case Hit::B1: move_times += 1; break; case Hit::B2: move_times += 2; break; case Hit::B3: move_times += 3; break; case Hit::B4: move_times += 4; break; default: break; } } if (move_times < 3) move_times = 0; else move_times -= 3; int score {0}; for (std::size_t i {0}; i < move_times; i++) score += player[i]; std::cout << score << "\n"; return 0; } ``` ## 6. [基本資料型態] 結構 ### a150: 多邊形面積 外積大法好。 ```cpp= #include <iomanip> #include <iostream> struct Pos { double x {}; double y {}; }; double cross (const Pos& a, const Pos& b) { return a.x * b.y - b.x * a.y; } int main () { int count {}; std::cin >> count; Pos first {}; std::cin >> first.x >> first.y; Pos last {first}; double area {0}; while (--count) { Pos now {}; std::cin >> now.x >> now.y; area += cross(last, now); last = now; } area += cross(last, first); area /= -2; std::cout << std::setprecision(2) << std::fixed << area; return 0; } ``` ### a113: 99遊戲 ```cpp= #include <iostream> #include <string> #include <utility> #include <vector> int main () { constexpr std::size_t players_num {4}; constexpr std::size_t cards {13}; // pair<名字, 手牌> std::vector<std::pair<std::string, std::vector<std::string>>> players( players_num, {{}, std::vector<std::string>(cards)} ); for (auto& player : players) { std::cin >> player.first; for (auto card {player.second.rbegin()}; card != player.second.rend(); card++) { std::cin >> *card; } } bool reversed {false}; int num {0}; std::size_t current_player_id {0}; while (true) { auto& player {players[current_player_id]}; const std::string card {player.second.back()}; player.second.pop_back(); if (card == "A") num = 0; else if (card == "4") reversed = !reversed; else if (card == "5" || card == "J") {} else if (card == "10") num += (num > 99 - 10) ? -10 : 10; else if (card == "Q") num += (num > 99 - 20) ? -20 : 20; else if (card == "K") num = 99; else num += std::stoi(card); if (num > 99) { std::cout << player.first << "\n" << player.second.size() << "\n"; return 0; } else if (player.second.empty()) { std::cout << player.first << "\n" << num << "\n"; return 0; } if (reversed) current_player_id += players_num - 1; else current_player_id += 1; current_player_id %= players_num; } return 0; } ``` ### a111: 排隊 ```cpp= #include <algorithm> #include <iostream> #include <utility> #include <vector> int main () { std::size_t numberOfCustomers {}; std::cin >> numberOfCustomers; std::vector<std::pair<int, int>> customers(numberOfCustomers); for (auto& customer : customers) { std::cin >> customer.first >> customer.second; } std::size_t max_line_long {0}; int clock {0}; std::size_t alreadyComing {0}; for (std::size_t i {0}; i < numberOfCustomers; i++) { if (clock < customers[i].first) clock = customers[i].first; clock += customers[i].second; if (alreadyComing < i) alreadyComing = i; for (std::size_t j {alreadyComing}; customers[j].first <= clock && j < numberOfCustomers; j++) alreadyComing = j; if (max_line_long < alreadyComing - i) max_line_long = alreadyComing - i; } std::cout << max_line_long << "\n"; return 0; } ``` ### a112: 動物數量統計 自己寫一個有序映射結構。 ```cpp= #include <iostream> #include <map> #include <vector> template <typename Key, typename T> struct OrderMap { std::map<Key, T> content {}; std::vector<Key> order {}; T& operator[] (const Key& key) { const auto& p {content.find(key)}; if (p == content.end()) { order.push_back(key); return content[key]; } return p->second; } const T& at (const Key& key) const { return content.at(key); } }; int main () { int lines {}; std::cin >> lines; OrderMap<std::string, OrderMap<std::string, int>> locations {}; while (lines--) { std::string animal {}; int numbers {}; std::string location {}; std::cin >> animal >> numbers >> location; locations[location][animal] += numbers; } for (const auto& location : locations.order) { std::cout << location << ":"; const auto& animals {locations.at(location)}; bool first {true}; for (const auto& animal : animals.order) { if (first) first = false; else std::cout << ","; std::cout << animal << " " << animals.at(animal); } std::cout << "\n"; } return 0; } ``` ## 7. [基礎資料結構 I ] 堆疊、佇列 ### a151: 後序式求值 ```cpp= #include <cctype> #include <iostream> #include <stack> #include <string> int main () { std::string line {}; while (std::getline(std::cin, line)) { std::stack<int> stack {}; for (const auto& c : line) { if (std::isdigit(c)) { stack.push(c - '0'); continue; } int b {stack.top()}; stack.pop(); int a {stack.top()}; stack.pop(); switch (c) { case '+': stack.push(a + b); break; case '-': stack.push(a - b); break; case '*': stack.push(a * b); break; case '/': stack.push(a / b); break; case '%': stack.push(a % b); break; } } std::cout << stack.top() << "\n"; } return 0; } ``` ### a120: 中置式轉後置式 有些語意問題,不想改了。 ```cpp= #include <iostream> #include <stack> enum class Operator { Add, Minus, Multipy, Divide, LeftBrace, RightBrace, Err }; bool get_op_priority (Operator left, Operator right) { if ( (right == Operator::Divide || right == Operator::Multipy) && (left == Operator::Add || left == Operator::Minus) ) return false; if ( left == Operator::LeftBrace || right == Operator::LeftBrace ) return false; return true; } Operator prase_op (char c) { return c == '+' ? Operator::Add : c == '-' ? Operator::Minus : c == '*' ? Operator::Multipy : c == '/' ? Operator::Divide : c == '(' ? Operator::LeftBrace : c == ')' ? Operator::RightBrace : Operator::Err; } char op_to_char (Operator op) { return op == Operator::Add ? '+' : op == Operator::Minus ? '-' : op == Operator::Multipy ? '*' : op == Operator::Divide ? '/' : '?'; } int main() { std::string line {}; std::stack<Operator> ops {}; char c {}; while (std::cin >> c) { const Operator op {prase_op(c)}; if (op == Operator::Err) { std::cout << c; continue; } if (op == Operator::RightBrace) { while (ops.top() != Operator::LeftBrace) { std::cout << op_to_char(ops.top()); ops.pop(); } ops.pop(); continue; } while (!ops.empty() && get_op_priority(ops.top(), op)) { std::cout << op_to_char(ops.top()); ops.pop(); } ops.push(op); } while (!ops.empty()) { std::cout << op_to_char(ops.top()); ops.pop(); } std::cout << "\n"; return 0; } ``` ### a119: 括號問題 只有一種括號能搞出什麼花樣。(汗 僅一種取值的堆疊所剩的資訊只有長度,無異於一個整型變數。 ```cpp= #include <iostream> int main () { int level {}; int number {}; char c {}; while (std::cin >> c) { if (c == '(') { level++; number++; } else { level--; if (level < 0) break; } } if (level != 0) number = 0; std::cout << number << "\n"; return 0; } ``` ### a276: 撲克牌遊戲:手風琴 順便練習一下疊代器的使用。 ```cpp= #include <iostream> #include <stack> #include <utility> #include <vector> using piles_t = std::vector<std::stack<std::pair<char, char>>>; bool match (const piles_t::iterator& a, const piles_t::iterator& b) { return a->top().first == b->top().first || a->top().second == b->top().second; } bool try_move (piles_t& piles) { for (auto from {piles.begin() + 1}; from != piles.end(); from++) { auto to {from}; while (true) { const auto i {to - piles.begin()}; if (i >= 3 && match(from, to - 3)) to -= 3; else if (i >= 1 && match(from, to - 1)) to -= 1; else break; } if (to == from) continue; to->push(from->top()); from->pop(); if (from->empty()) piles.erase(from); return true; } return false; } int main () { constexpr std::size_t cards_num {52}; while (true) { piles_t piles(cards_num); for (auto& pile : piles) { char rank {}, suit {}; std::cin >> rank >> suit; if (rank == '#') return 0; pile.push({rank, suit}); } while (try_move(piles)) {} if (piles.size() == 1) std::cout << "1 pile remaining:"; else std::cout << piles.size() << " piles remaining:"; for (const auto& pile : piles) { std::cout << " " << pile.size(); } std::cout << "\n"; } } ``` ### a074: 單線鐵路 無需儲存 A 端狀態,因為固定順序且無法回頭。亦無需儲存 B,因為完成的已與後續無關。 這是第一個 `fastio()` 能給出顯著提升的題目,就順道加了。 ```cpp= #include <iostream> #include <stack> void fastio () { std::ios_base::sync_with_stdio(false); std::cin.tie(nullptr); } bool solve (int cars_num) { std::stack<int> station {}; int front_car {1}; // A 端最外面一臺之編號 bool fail {false}; while (cars_num--) { int n {}; std::cin >> n; if (!n) return false; if (fail) continue; if (!station.empty() && station.top() == n) { station.pop(); continue; } while (front_car < n) station.push(front_car++); if (front_car != n) fail = true; else front_car++; } if (fail) std::cout << "No\n"; else std::cout << "Yes\n"; return true; } int main () { fastio(); int cars_num {}; std::cin >> cars_num; while (cars_num) { while (solve(cars_num)) {} std::cin >> cars_num; } } ``` ### a163: 印表機佇列 ||~~前一題還在 90%,稍後補。~~|| 頭放尾、頭放尾、條件符合不放尾,頭放尾移動整個陣列不就相當於一動視線嗎?盡管這題名叫「佇列」,但用佇列解才是最麻煩的,其操作本質上顯然是「(單向)環狀鏈結串列」。 ```cpp= #pragma GCC optimize("O3") #include <array> #include <forward_list> #include <iostream> template <typename T> constexpr std::size_t u(T i) { return static_cast<std::size_t>(i); } int main () { constexpr int max_priority {9}; int tests {}; std::cin >> tests; while (tests--) { std::array<int, max_priority> priority_count {}; int length {}, target_i {}; std::cin >> length >> target_i; std::forward_list<int> notaqueue {}; auto last {notaqueue.before_begin()}; auto target {last}; int priority {}; for (int i = 0; i < length; i++) { int file {}; std::cin >> file; priority_count[u(file - 1)]++; last = notaqueue.insert_after(last, file); if (i == target_i) target = last; if (file > priority) priority = file; } int time {0}; while (true) { auto next {std::next(last)}; if (next == notaqueue.end()) last = notaqueue.before_begin(); else if (*next == priority) { time++; if (next == target) break; notaqueue.erase_after(last); priority_count[u(priority - 1)]--; while (!priority_count[u(priority - 1)]) priority--; } else last = next; } std::cout << time << "\n"; } } ``` 甚至這題都還不用真的使用鏈結串列,因為並沒有必要真的「擦除」印出來的東西,優先權打掉就好。反正只有九種優先權,仔細想想就會發現循環次數並不會超過九次。 ```cpp= #pragma GCC optimize("O3") #include <array> #include <iostream> #include <vector> template <typename T> constexpr std::size_t u(T i) { return static_cast<std::size_t>(i); } int main () { constexpr int max_priority {9}; int tests {}; std::cin >> tests; while (tests--) { std::array<int, max_priority> priority_count {}; int length {}, target {}; std::cin >> length >> target; std::vector<int> notaqueue(u(length)); int priority {}; for (int i = 0; i < length; i++) { int file {}; std::cin >> file; priority_count[u(file - 1)]++; notaqueue[u(i)] = file; if (file > priority) priority = file; } int time {0}; int index {0}; while (true) { if (notaqueue[u(index)] == priority) { time++; if (index == target) break; notaqueue[u(index)] = 0; priority_count[u(priority - 1)]--; while (!priority_count[u(priority - 1)]) priority--; } index = (index + 1) % length; } std::cout << time << "\n"; } } ``` ### a164: 團體佇列 ```cpp= #include <iostream> #include <map> #include <queue> #include <string> int main () { int id { 1 }; while (true) { std::map<int, int> member_groups {}; int group_num {}; std::cin >> group_num; if (group_num == 0) return 0; std::cout << "Scenario #" << id++ << '\n'; for (int group_id { 0 }; group_id < group_num; group_id++) { int member_num {}; std::cin >> member_num; while (member_num--) { int member {}; std::cin >> member; member_groups.emplace(member, group_id); } } std::map<int, std::queue<int>> grounps {}; std::queue<int> grounp_queue {}; while (true) { std::string command; std::cin >> command; if (command == "STOP") break; if (command == "ENQUEUE") { int ele {}; std::cin >> ele; const int grounp_id { member_groups.at(ele) }; auto& grounp { grounps[grounp_id] }; if (grounp.empty()) grounp_queue.push(grounp_id); grounp.push(ele); } else if (command == "DEQUEUE") { auto& grounp { grounps.at(grounp_queue.front()) }; std::cout << grounp.front() << '\n'; grounp.pop(); if (grounp.empty()) grounp_queue.pop(); } } std::cout << '\n'; } } ``` ## 8. [基礎資料結構 I ] 樹狀結構 ### a077: 小球落下 解釋待補.jpg ```cpp= #include <iostream> int main () { int tests {}; std::cin >> tests; while (tests--) { unsigned int depth {}, bolls {}; std::cin >> depth >> bolls; bolls--; unsigned int ans {1}; while (--depth) { ans <<= 1; ans |= bolls & 1; bolls >>= 1; } std::cout << ans << '\n'; } return 0; } ``` ### a076: 二元搜尋樹高度 比起硬壓,真寫一個二元樹感覺才是最合適的。 ```cpp= #include <algorithm> #include <iostream> struct BST { int value {}; BST* left {nullptr}; BST* right {nullptr}; int insert (int n, int deep = 1) { auto& next {n <= value ? left : right}; if (next != nullptr) return next->insert(n, deep + 1); next = new BST {n}; return deep + 1; } }; int main () { int n {}; std::cin >> n; BST root {n}; int maxdeep {0}; while (std::cin >> n) maxdeep = std::max(maxdeep, root.insert(n)); std::cout << maxdeep << '\n'; return 0; } ``` ### a122: 血緣關係 分治大法好。另外提醒基於樹為無環連通圖,任何一點都可做為根結點。 ```cpp= #pragma GCC optimize("O3") #include <iostream> #include <tuple> #include <utility> #include <vector> // C++14 都十年了還用啥前處理器巨集 using tree_t = std::vector<std::vector<int>>; constexpr void max_assign (int& a, int b) { if (a < b) a = b; } constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } std::pair<int, int> max_distance_and_deep (const tree_t& tree, int node = 0) { int deep_first {0}; int deep_second {0}; int max_distance {0}; for (const auto& child : tree[u(node)]) { int distence {}, deep {}; std::tie(distence, deep) = max_distance_and_deep(tree, child); max_assign(max_distance, distence); if (deep >= deep_first) { deep_second = deep_first; deep_first = deep; } else max_assign(deep_second, deep); } max_assign(max_distance, deep_first + deep_second); return {max_distance, deep_first + 1}; } int main () { fastio(); int nodes_num {}; std::cin >> nodes_num; tree_t tree(u(nodes_num)); // vector<bool> std::vector<char> has_parent(u(nodes_num), false); for (int i {1}; i < nodes_num; i++) { int a {}, b {}; std::cin >> a >> b; tree[u(a)].push_back(b); has_parent[u(b)] = true; } int root {-1}; while (has_parent[u(++root)]) {} std::cout << max_distance_and_deep(tree, root).first << '\n'; return 0; } ``` ### a123: 樹狀圖分析 跟上一題幾乎一模一樣,不過小改了幾個地方的寫法。 ```cpp= #pragma GCC optimize("O3") #include <iostream> #include <utility> #include <vector> using tree_t = std::vector<std::vector<int>>; constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } std::pair<int, int> max_deepsum_and_deep (const tree_t& tree, int node) { int max_deep {0}; int deep_sum_sum {0}; for (const auto& child : tree[u(node - 1)]) { const auto& child_data {max_deepsum_and_deep(tree, child)}; if (max_deep < child_data.second) max_deep = child_data.second; deep_sum_sum += child_data.first; } return {deep_sum_sum + max_deep, max_deep + 1}; } int main () { fastio(); int nodes_num {}; std::cin >> nodes_num; tree_t tree(u(nodes_num)); // vector<bool> std::vector<char> has_parent(u(nodes_num), false); for (auto& node : tree) { int child_num {}; std::cin >> child_num; while (child_num--) { int child {}; std::cin >> child; has_parent[u(child - 1)] = true; node.push_back(child); } } int root {0}; while (has_parent[u(root++)]) {} std::cout << root << '\n' << max_deepsum_and_deep(tree, root).first << '\n'; return 0; } ``` ## 9. [基礎資料結構 I ] 圖形結構 ### a051: 城市旅遊 此乃深度優先搜尋。 ```cpp= #pragma GCC optimize("O3") #include <bitset> #include <iostream> #include <vector> constexpr int max_cities {800}; using graph_t = std::vector<std::vector<int>>; constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } bool dfs (const graph_t& graph, int target, int from, std::bitset<max_cities>& visited) { visited.set(u(from - 1)); for (const auto& to : graph[u(from - 1)]) { if ( to == target || !visited.test(u(to - 1)) && dfs(graph, target, to, visited) ) return true; } return false; } int main () { fastio(); int cities_num {}, roads_num {}; std::cin >> cities_num >> roads_num; graph_t graph(u(cities_num)); while (roads_num--) { int from {}, to {}; std::cin >> from >> to; graph[u(from - 1)].push_back(to); } int from {}, target {}; std::cin >> from >> target; std::bitset<max_cities> visited {}; if (dfs(graph, target, from, visited)) std::cout << "Yes\n"; else std::cout << "No\n"; return 0; } ``` ### a102: 油田 是的,完全沒在最佳化。 ```cpp= #pragma GCC optimize("O3") #include <bitset> #include <iostream> constexpr int max_width {100 + 2}; using ground_t = std::bitset<max_width * max_width>; constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } void clear_nearby (ground_t& ground, int pos) { static constexpr int near[] { -max_width - 1, -max_width, -max_width + 1, -1, +1, +max_width - 1, +max_width, +max_width + 1 }; if (!ground[u(pos)]) return; ground[u(pos)] = false; for (const auto& diff : near) clear_nearby(ground, pos + diff); } int main () { fastio(); while (true) { int rows {}, colums {}; std::cin >> rows >> colums; if (rows == 0) return 0; ground_t ground {}; for (int y {1}; y <= rows; y++) { for (int x {1}; x <= colums; x++) { char symbol {}; std::cin >> symbol; if (symbol == '@') ground[u(y * max_width + x)] = true; } } int number {0}; while (true) { int pos {}; while (pos < ground.size() && !ground[u(pos)]) pos++; if (pos == ground.size()) break; number++; clear_nearby(ground, pos); } std::cout << number << '\n'; } } ``` ### a103: 小群體 ```cpp= #pragma GCC optimize("O3") #include <iostream> #include <vector> constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } int main () { fastio(); int people {}; std::cin >> people; std::vector<int> friend_map(people); for (auto& frind : friend_map) std::cin >> frind; int grounps {}; for (int i = 0; i < people; i++) { int pos {i}; do { const int temp {friend_map[u(pos)]}; friend_map[u(pos)] = -1; pos = temp; } while (pos != -1 && pos != i); if (pos != -1) grounps++; } std::cout << grounps << '\n'; return 0; } ``` ### a124: 二分圖 題目描述直接給思路是不是太過貼心了。 還有另一種更好但更麻煩的解法懶得弄了。 ```cpp= #pragma GCC optimize("O3") #include <algorithm> #include <iostream> #include <stack> #include <vector> template <typename T> constexpr std::size_t u (T i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } int main () { fastio(); int vertices_num {}, edges_num {}; std::cin >> vertices_num >> edges_num; std::vector<std::vector<int>> graph(u(vertices_num)); while (edges_num--) { int from {}, to {}; std::cin >> from >> to; graph[u(from - 1)].push_back(to - 1); graph[u(to - 1)].push_back(from - 1); } // std::vector<bool> std::vector<char> visited(u(vertices_num)); std::vector<char> color(u(vertices_num)); int answer {0}; while (true) { int id {-1}; while (++id < vertices_num && visited[u(id)]) {} if (id == vertices_num) break; std::stack<int> to_visit {}; to_visit.push(id); int white {1}; int black {0}; visited[u(id)] = true; while (!to_visit.empty()) { const int now {to_visit.top()}; to_visit.pop(); for (const auto& near : graph[u(now)]) { if (visited[u(near)] && color[u(near)] == color[u(now)]) { std::cout << "0\n"; return 0; } if (visited[u(near)]) continue; visited[u(near)] = true; color[u(near)] = !color[u(now)]; if (color[u(near)]) black++; else white++; to_visit.push(near); } } answer += std::min(black, white); } std::cout << answer << '\n'; return 0; } ``` ### a125: 觀光景點 ```cpp= #pragma GCC optimize("O3") #include <iostream> #include <stack> #include <utility> #include <vector> #include <bitset> template <typename T> constexpr std::size_t u (T i) { return static_cast<std::size_t>(i); } void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } int main () { fastio(); int vertices_num {}, root {}, max_cost {}; std::cin >> vertices_num >> root >> max_cost; std::vector<std::vector<std::pair<int, int>>> graph(u(vertices_num)); for (int i {0}; i < vertices_num - 1; i++) { int from {}, to {}, cost {}; std::cin >> from >> to >> cost; graph[u(from - 1)].emplace_back(to - 1, cost); graph[u(to - 1)].emplace_back(from - 1, cost); } std::stack<int> to_visit {}; std::bitset<5000> visited {}; visited.set(u(root - 1)); to_visit.push(root - 1); int answer {0}; while (!to_visit.empty()) { const int vertex {to_visit.top()}; to_visit.pop(); for (const auto& edge : graph[u(vertex)]) { if (visited[u(edge.first)] || edge.second < max_cost) continue; answer++; to_visit.push(edge.first); visited.set(u(edge.first)); } } std::cout << answer << '\n'; return 0; } ``` ### a126: 馬步問題 嘛不是很好看。罷了。 ```cpp= #include <bitset> #include <iostream> #include <stack> #include <vector> constexpr inline std::size_t u (int i) { return static_cast<std::size_t>(i); } int main () { static constexpr int dx[] { 1, 1, -1, -1, 2, 2, -2, -2 }; static constexpr int dy[] { 2, -2, 2, -2, 1, -1, 1, -1 }; constexpr int row {3}; constexpr int max_colum {10}; int colum {}; std::cin >> colum; const int length {colum * row}; std::vector<int> way {}; std::vector<int> answer(u(length), 100); std::stack<int> chooses {}; std::bitset<row* max_colum> visited {}; chooses.push(-1); way.push_back(0); visited[0] = true; while (!chooses.empty()) { const int choose {++chooses.top()}; if (choose == 8) { chooses.pop(); visited[u(way.back())] = false; way.pop_back(); continue; } const int x {way.back() % colum + dx[choose]}; const int y {way.back() / colum + dy[choose]}; const int pos {y * colum + x}; if (x >= colum || x < 0 || y >= row || y < 0 || visited[u(pos)]) continue; way.push_back(pos); visited[u(pos)] = true; if (way.size() != length) { chooses.push(-1); continue; } std::vector<int> temp(u(length)); for (int i = 0; i < length; i++) temp[u(way[u(i)])] = i + 1; if (temp < answer) answer = temp; chooses.push(7); } if (answer[0] == 100) { std::cout << "0\n"; return 0; } for (const auto& x : answer) std::cout << x << ' '; std::cout << '\n'; return 0; } ``` ## 10. [基礎演算法 I ] 複雜度分析、排序 ### a127: 連號或不連號 我宣布這是最簡單的一題,沒有之一。 ```cpp= #include <iostream> int main () { int num {}; std::cin >> num; int min {1000}; int max {0}; for (int i = 0; i < num; i++) { int number {}; std::cin >> number; if (max < number) max = number; if (min > number) min = number; } std::cout << min << ' ' << max << ' ' << (max - min + 1 == num ? "yes" : "no"); return 0; } ``` ### a148: 字元頻率 糾結了一下要用 `std::vector` + `std::sort` 還是直接用 `std::priority_queue`。 ```cpp= #include <algorithm> #include <iostream> #include <map> #include <string> #include <utility> #include <vector> int main () { std::string line {}; while (std::getline(std::cin, line)) { std::map<int, int> nums {}; for (const auto& c : line) nums[c]++; std::vector<std::pair<int, int>> answer(nums.begin(), nums.end()); std::sort(answer.begin(), answer.end(), [](const auto& a, const auto& b) { return a.second == b.second ? a.first > b.first : a.second < b.second; }); for (const auto& ele : answer) std::cout << ele.first << ' ' << ele.second << '\n'; std::cout << '\n'; } return 0; } ``` ### a128: Agar.io ```cpp= #include <algorithm> #include <iostream> #include <vector> template<typename T> void merge (std::vector<T>& to, const std::vector<T>& from) { to.insert(to.end(), from.begin(), from.end()); } int main () { std::size_t number {}; int times {}; std::cin >> number >> times; std::vector<std::vector<int>> cells(number); for (int i {0}; i < number; i++) cells[static_cast<std::size_t>(i)].push_back(i + 1); while (times--) { std::size_t a {}, b {}; std::cin >> a >> b; a--; b--; if (cells[b].size() > cells[a].size()) merge(cells[b], cells[a]); else merge(cells[a], cells[b]); } const auto max {std::max_element(cells.begin(), cells.end(), [](const auto& a, const auto& b) { return a.size() < b.size(); })}; std::cout << max - cells.begin() + 1 << '\n'; std::sort(max->begin(), max->end()); for (const auto& eat : *max) std::cout << eat << ' '; std::cout << '\n'; return 0; } ``` ### a129: 飛天桑妮 這題其實我少考慮情況 $d(t_1, t_{\pi(i+1)}) = d(t_1, t_{\pi(i)})$,不過顯然寫測資的人也沒考慮到,所以管他的。 ```cpp= #include <algorithm> #include <iostream> #include <utility> #include <vector> void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } int main () { fastio(); std::size_t trees_num {}; std::cin >> trees_num; std::vector<std::pair<int, int>> trees(trees_num); for (auto& tree : trees) { int x {}, y {}, hight {}; std::cin >> x >> y >> hight; tree.first = x * x + y * y; tree.second = hight; } std::sort(trees.begin(), trees.end()); int happyness {0}; int highest {0}; for (const auto& tree : trees) { highest = std::max(highest, tree.second); happyness = std::max(happyness, highest - tree.second); } std::cout << happyness << '\n'; return 0; } ``` ## 11. [基礎演算法 I ] 排序與搜尋 ### a152: 二分搜尋 就先不邪道了,乖乖寫。 ```cpp= #include <iostream> #include <vector> int main () { std::size_t length {}; std::cin >> length; std::vector<int> numbers(length); for (auto& number : numbers) std::cin >> number; int target {}; std::cin >> target; std::size_t left {0}; // closed std::size_t right {length}; // open int times {0}; while (left != right) { times++; const std::size_t middle {(right - left + 1) / 2 + left - 1}; if (numbers[middle] > target) right = middle; else if (numbers[middle] < target) left = middle + 1; else { std::cout << middle << ' ' << times << '\n'; return 0; } } std::cout << "not found " << times << '\n'; return 0; } ``` ### a153: 二分法求解 我知道大家都有種寫 `std::log(2)` 的衝動,不過姑且假裝自己不知道函式長怎樣一下。 ```cpp= #include <cmath> #include <iostream> using num_t = double; num_t f (num_t x) { return 2 - std::exp(x); } constexpr num_t bigger {1}; constexpr num_t lower {0}; constexpr num_t TOL {1.0e-8}; int main () { num_t max {bigger}; num_t min {lower}; while (std::abs(f(max) - f(min)) > TOL) { const num_t middle {(min + max) / 2}; if (f(middle) > 0 == f(min) > 0) min = middle; else max = middle; } std::cout << min << '\n'; return 0; } ``` ### a290: 完美購書計畫 很醜。 ```cpp= #include <map> #include <iostream> int main () { int books_num {}; while (std::cin >> books_num) { std::map<int, int> books {}; while (books_num--) { int book {}; std::cin >> book; books[book]++; } int target {}; std::cin >> target; const int halfTarget {target / 2}; int answer {target + 1}; if (target % 2 == 0) { const auto half {books.find(halfTarget)}; if (half != books.end()) { if (half->second >= 2) answer = 0; else books.erase(half); } } for (auto book {books.begin()}; book != books.end(); books.erase(book++)) { if (!answer || book->first > halfTarget) break; const auto need {books.find(target - book->first)}; if (need == books.end()) continue; books.erase(need); answer = std::min(answer, halfTarget - book->first); } std::cout << "Peter should buy books whose prices are " << halfTarget - answer << " and " << halfTarget + answer + target % 2 << ".\n\n"; } return 0; } ``` ### a130: 人員調動 ```cpp= #include <iostream> #include <set> #include <utility> int main () { int tests {}; std::cin >> tests; while (tests--) { int peoples {}; std::cin >> peoples; std::multiset<std::pair<int, int>> pairs {}; int count {}; while (peoples--) { int from {}, to {}; std::cin >> from >> to; const auto reverse {pairs.find({to, from})}; if (reverse == pairs.end()) { pairs.emplace(from, to); } else { count++; pairs.erase(reverse); } } std::cout << count << '\n'; } return 0; } ``` ### a131: 大黑馬 來點比較舒服的寫法。 ```cpp= #include <iostream> #include <vector> constexpr inline std::size_t u (int i) { return static_cast<std::size_t>(i); } struct Team { int seed {}; int length {}; std::vector<int> average {}; std::vector<int> best {}; std::vector<int> worse {}; Team (int length) : length {length}, average(u(length)), best(u(length)), worse(u(length)) {} }; bool canBeat (const Team& team, const Team& opponent, bool chance) { if (team.seed == opponent.seed) return true; const int half {team.length / 2}; const auto& our_players {chance ? team.best : team.average}; const auto& opponent_players {chance ? opponent.worse : opponent.average}; for (int i {0}; i < half + 1; i++) { const int our_bp {our_players[u(i)]}; const int opponent_bp {opponent_players[u(half + i)]}; if (opponent_bp > our_bp || opponent_bp == our_bp && opponent.seed < team.seed) return false; } return true; } bool canWin (const Team& team, const std::vector<Team>& teams, bool chance) { for (const auto& opponent : teams) { if (!canBeat(team, opponent, chance)) return false; } return true; } int getBlackHorse (const std::vector<Team>& teams, bool chance) { int blackHorse {0}; for (const auto& team : teams) { if (team.seed > blackHorse && canWin(team, teams, chance)) blackHorse = team.seed; } return blackHorse; } int main () { int players_half {}, teams_num {}; std::cin >> players_half >> teams_num; std::vector<Team> teams(u(teams_num), Team(players_half * 2 + 1)); for (auto& team : teams) { std::cin >> team.seed; for (auto& bp : team.average) std::cin >> bp; for (auto& bp : team.best) std::cin >> bp; for (auto& bp : team.worse) std::cin >> bp; } std::cout << getBlackHorse(teams, false) << ' ' << getBlackHorse(teams, true) << '\n'; return 0; } ``` ### a132: 主機排程 ```cpp= #include <iostream> #include <map> #include <utility> struct Cost { int core {0}; int ram {0}; int bw {0}; Cost& operator+= (const Cost& b) { core += b.core; ram += b.ram; bw += b.bw; return *this; } Cost& operator-= (const Cost& b) { core -= b.core; ram -= b.ram; bw -= b.bw; return *this; } void max (const Cost& b) { if (core < b.core) core = b.core; if (ram < b.ram) ram = b.ram; if (bw < b.bw) bw = b.bw; } }; int main () { int tests {}, divisor {}; std::cin >> tests >> divisor; divisor *= 24; while (tests--) { int processes_num {}; std::cin >> processes_num; std::map<int, Cost> costs {}; Cost now_cost {0, 0, 0}; while (processes_num--) { int start_d {}, start_h {}, time {}; std::cin >> start_d >> start_h >> time; const int start {(start_d - 1) * 24 + start_h}; const int end {start + time}; Cost cost {}; std::cin >> cost.core >> cost.ram >> cost.bw; costs[start] += cost; costs[end % divisor] -= cost; if (end >= divisor) now_cost += cost; } std::cin >> processes_num; // ignore tail 0 Cost max_cost {now_cost}; for (const auto& cost : costs) { now_cost += cost.second; max_cost.max(now_cost); } std::cout << max_cost.core << ' ' << max_cost.ram << ' ' << max_cost.bw << '\n'; } return 0; } ``` ## 12. [基礎演算法 I ] 窮舉法 ### a088: 最大乘積 切三段分析法,另一種比較漂亮的等價方法之後再補。 ```cpp= #include <algorithm> #include <iostream> int main () { int test {1}; int num {}; while (std::cin >> num) { long long max_product {0}; long long middle {1}, last {1}, first {0}; bool avalible {false}; do { int next {0}; if (num) std::cin >> next; if (next == 0) { if (avalible) max_product = std::max({max_product, first * middle, last * middle, first * last * middle}); first = 0; middle = 1; last = 1; avalible = false; } else if (next > 0) { last *= next; avalible = true; } else if (!first) { if (avalible) max_product = std::max(max_product, last); first = last * next; last = 1; } else { middle *= last; last = next; avalible = true; } } while (num--); std::cout << "Case #" << test++ << ": The maximum product is " << max_product << ".\n\n"; } return 0; } ``` ### a133: 採蘑菇攻略問題 ```cpp= #include <iostream> #include <utility> int main () { int num {}; std::cin >> num; int sum {0}; int max_sum {0}; while (num--) { int number {}; std::cin >> number; sum = std::max(0, sum + number); max_sum = std::max(max_sum, sum); } std::cout << max_sum << '\n'; return 0; } ``` ### a154: 除法 使用乘法直式的概念一位一位算,進行回朔與剪枝。 嗯……`result` 和 `answers` 的用法的確很噁心,有空我再想想怎改。 ```cpp= #include <algorithm> #include <iomanip> #include <iostream> #include <vector> void check (int target, unsigned int used, int length, int carry, std::vector<int>& result, std::vector<int>& answers) { if (length == 5) { if (carry != 0) return; int x {0}; for (auto i {result.rbegin()}; i != result.rend(); i++) x = x * 10 + *i; answers.push_back(x); return; } for (int i {0}; i <= 9; i++) { if (used & (1 << i)) continue; const int product {i * target + carry}; const int j {product % 10}; if (i == j || used & (1 << j)) continue; result.push_back(j); check(target, used | (1 << i) | (1 << j), length + 1, product / 10, result, answers); result.pop_back(); } } int main () { std::cout << std::setfill('0'); while (true) { int target {}; std::cin >> target; if (!target) return 0; std::vector<int> answers {}; std::vector<int> result {}; check(target, 0, 0, 0, result, answers); if (answers.empty()) { std::cout << "There are no solutions for " << target << ".\n"; continue; } std::sort(answers.begin(), answers.end()); for (const auto& answer : answers) { std::cout << std::setw(5) << answer << " / " << std::setw(5) << answer / target << " = " << target << '\n'; } } } ``` ### a134: 回文日期問題 ```cpp= #include <algorithm> #include <ctime> #include <iostream> #include <string> #include <vector> void is_palindrome (const std::string& str, std::vector<int>& output) { for (std::size_t i {0}; i < str.length() / 2 + 1; i++) { if (str[i] != str[str.length() - i - 1]) return; } output.push_back(std::stoi(str)); } int main () { int tests {}; std::cin >> tests; while (tests--) { int year {}; std::cin >> year; const std::string yearstr {std::to_string(year)}; year = (year + 100) % 1000; std::tm date {}; date.tm_year = year; date.tm_mday = 1; std::vector<int> answers {}; while (date.tm_year == year) { const std::string monstr {std::to_string(date.tm_mon + 1)}; const std::string daystr {std::to_string(date.tm_mday)}; is_palindrome(yearstr + monstr + daystr, answers); if (monstr.length() == 1) is_palindrome(yearstr + "0" + monstr + daystr, answers); if (daystr.length() == 1) is_palindrome(yearstr + monstr + "0" + daystr, answers); if (monstr.length() == 1 && daystr.length() == 1) is_palindrome(yearstr + "0" + monstr + "0" + daystr, answers); date.tm_mday++; std::mktime(&date); } std::sort(answers.begin(), answers.end()); std::cout << answers.size(); for (const auto& answer : answers) std::cout << ' ' << answer; std::cout << '\n'; } return 0; } ``` ### a135: 巧克力擺盒 位元運算大法好,效率特高。 ```cpp= #include <algorithm> #include <iostream> #include <numeric> #include <utility> #include <vector> struct Row { std::pair<char, char> a {}; std::pair<char, char> b {}; std::pair<char, char> c {}; unsigned int used {0}; unsigned int conditions {0}; }; int main () { std::vector<std::pair<char, char>> chocolates {}; for (const auto& f : {'B', 'P', 'Y'}) { for (const auto& s : {'C', 'S', 'T'}) chocolates.emplace_back(f, s); } std::vector<Row> rows {}; std::vector<unsigned int> nums(9); std::iota(nums.begin(), nums.end(), 0); do { rows.push_back({ chocolates[nums[0]], chocolates[nums[1]], chocolates[nums[2]], static_cast<unsigned int>(1 << nums[0] | 1 << nums[1] | 1 << nums[2]) }); std::reverse(nums.begin() + 3, nums.end()); } while (std::next_permutation(nums.begin(), nums.end())); int rules_num {}; std::cin >> rules_num; unsigned int pass_all {0}; unsigned int pass {1}; while (rules_num--) { int type {}; std::cin >> type; if (type == 2) { char a_f {}, a_s {}, b_f {}, b_s {}; std::cin >> a_f >> a_s >> b_f >> b_s; for (auto& row : rows) { if ( (a_f == '?' || a_f == row.a.first) && (a_s == '?' || a_s == row.a.second) && (b_f == '?' || b_f == row.b.first) && (b_s == '?' || b_s == row.b.second) || (a_f == '?' || a_f == row.b.first) && (a_s == '?' || a_s == row.b.second) && (b_f == '?' || b_f == row.c.first) && (b_s == '?' || b_s == row.c.second) ) row.conditions |= pass; } } else { char a_f {}, a_s {}, b_f {}, b_s {}, c_f {}, c_s {}; std::cin >> a_f >> a_s >> b_f >> b_s >> c_f >> c_s; for (auto& row : rows) { if ( (a_f == '?' || a_f == row.a.first) && (a_s == '?' || a_s == row.a.second) && (b_f == '?' || b_f == row.b.first) && (b_s == '?' || b_s == row.b.second) && (c_f == '?' || c_f == row.c.first) && (c_s == '?' || c_s == row.c.second) ) row.conditions |= pass; } } pass_all |= pass; pass <<= 1; } int answer {0}; for (auto a {rows.begin()}; a != rows.end(); a++) { for (auto b {a + 1}; b != rows.end(); b++) { if (a->used & b->used) continue; const unsigned int condition_ab {a->conditions | b->conditions}; const unsigned int used_ab {a->used | b->used}; for (auto c {b + 1}; c != rows.end(); c++) { if (!(used_ab & c->used) && (condition_ab | c->conditions) == pass_all) answer += 6; } } } std::cout << answer << '\n'; return 0; } ``` ## 13. [基礎資料結構 II 及基礎演算法 II ] 貪婪法 ### a091: 加總的代價 ```cpp= #include <iostream> #include <queue> #include <vector> int main () { while (true) { int numbers_count {}; std::cin >> numbers_count; if (!numbers_count) return 0; std::priority_queue<int, std::vector<int>, std::greater<int>> numbers {}; while (numbers_count--) { int number {}; std::cin >> number; numbers.push(number); } long long cost {0}; while (numbers.size() > 1) { int sum {numbers.top()}; numbers.pop(); sum += numbers.top(); numbers.pop(); numbers.push(sum); cost += sum; } std::cout << cost << '\n'; } } ``` ### a071: 排隊買飲料 真就照題目說的模擬。 ```cpp= #include <iostream> #include <vector> int main () { int customers_count {}; std::cin >> customers_count; std::size_t staffs_count {}; std::cin >> staffs_count; std::vector<int> staffs(staffs_count, 0); int clock {-1}; bool conti {true}; while (conti) { clock++; conti = false; for (auto& staff : staffs) { if (staff != 0) staff--; if (!staff && customers_count) { std::cin >> staff; customers_count--; conti = true; } else if (staff) conti = true; } } std::cout << clock << '\n'; return 0; } ``` ### a141: 基地台 所以[二分法求解](#a153-二分法求解)為什麼不用這題? ```cpp= #include <algorithm> #include <cmath> #include <iostream> #include <set> #include <stdexcept> #include <vector> void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } bool is_distance_possible (const std::vector<int>& points, int max_number, int distance) { int number {1}; int left_point {points[0]}; for (const auto& point : points) { if (point - left_point <= distance) continue; number++; if (number > max_number) return false; left_point = point; } return true; } int main() { fastio(); std::set<int> possible_disances {}; int points_num {}; int max_number {}; std::cin >> points_num >> max_number; std::vector<int> points(points_num); for (auto& point : points) std::cin >> point; std::sort(points.begin(), points.end()); int max {points.back() - points.front()}; int min {0}; while (max - min > 1) { const int middle {(max + min) / 2}; if (is_distance_possible(points, max_number, middle)) max = middle; else min = middle; } std::cout << max; return 0; } ``` ### a139: 背包置物問題 ```cpp= #include <algorithm> #include <iostream> #include <istream> #include <set> #include <vector> int main() { int books_num {}; int max_carry {}; std::cin >> books_num >> max_carry; std::vector<int> needs {}; int input {}; while (std::cin >> input) { needs.push_back(max_carry); max_carry = input; } std::set<int> bag {}; int times {0}; for (auto need {needs.begin()}; need != needs.end(); need++) { if (bag.count(*need)) continue; if (bag.size() < max_carry) { bag.insert(*need); continue; } bag.erase(std::max_element(bag.begin(), bag.end(), [&need, &needs](int a, int b) { const auto a_next_use {std::find(need, needs.end(), a)}; const auto b_next_use {std::find(need, needs.end(), b)}; return a_next_use < b_next_use; })); bag.insert(*need); times++; } std::cout << times << '\n'; return 0; } ``` ### a075: 醜數 比較簡單的貪婪解: ```cpp= #include <iomanip> #include <iostream> #include <set> int main() { static const int factors[] {2, 3, 5}; int n {}; std::cin >> n; std::set<int> ugly {}; ugly.insert(1); while (--n) { const auto first {ugly.begin()}; for (const int x : {2, 3, 5}) ugly.insert(*first * x); ugly.erase(first); } std::cout << *ugly.begin(); return 0; } ``` 較為複雜但有極小的一咪咪提升的動態規劃解: ```cpp= #include <algorithm> #include <iostream> #include <vector> int main() { int n {}; std::cin >> n; std::vector<int> ugly {1}; ugly.reserve(n); auto i2 {ugly.begin()}, i3 {i2}, i5 {i2}; while (--n) { const int x2 {*i2 * 2}, x3 {*i3 * 3}, x5 {*i5 * 5}; int next = std::min({x2, x3, x5}); ugly.push_back(next); if (x2 == next) i2++; if (x3 == next) i3++; if (x5 == next) i5++; } std::cout << ugly.back() << '\n'; return 0; } ``` ## 14. [基礎資料結構 II 及基礎演算法 II ] 分而治之與回溯法 ### a165: 最近點對問題 就照課本說的寫。 ```cpp= #include <algorithm> #include <cmath> #include <iomanip> #include <iostream> #include <utility> #include <vector> void fastio () { std::cin.tie(nullptr)->sync_with_stdio(false); } constexpr long long max_distance {10000 * 10000 + 1}; using Point = std::pair<long long, long long>; long long distance (Point a, Point b) { const long long dx {a.first - b.first}; const long long dy {a.second - b.second}; return std::min(dx * dx + dy * dy, max_distance); } long long get_min_distance (const std::vector<Point>& points, std::size_t from, std::size_t to) { if (to == from) return max_distance; const std::size_t middle {(from + to) / 2}; long long min_distance {std::min(get_min_distance(points, from, middle), get_min_distance(points, middle + 1, to))}; for (std::size_t i {middle}; i >= from && i != -1; i--) { if (points[middle].first - points[i].first > min_distance) break; for (std::size_t j {middle + 1}; j <= to; j++) { if (points[j].first - points[middle].first > min_distance) break; min_distance = std::min(min_distance, distance(points[i], points[j])); } } return min_distance; } int main() { fastio(); std::cout << std::fixed << std::setprecision(4); while (true) { std::size_t points_num {}; std::cin >> points_num; if (!points_num) return 0; std::vector<Point> points(points_num); for (auto& point : points) std::cin >> point.second >> point.first; std::sort(points.begin(), points.end()); const long long min_distance {get_min_distance(points, 0, points.size() - 1)}; if (min_distance == max_distance) std::cout << "INFINITY\n"; else std::cout << std::sqrt(min_distance) << '\n'; } } ``` ### a089: 蘇丹王位繼承者 先解完八皇后再解題目。 ```cpp= #include <iomanip> #include <iostream> #include <vector> constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } bool check (const std::vector<int>& choose) { int y {static_cast<int>(choose.size() - 1)}; if (y == 0) return true; for (int i {0}; i < y; i++) { if ( choose.back() == choose[u(i)] || choose.back() + y == choose[u(i)] + i || choose.back() - y == choose[u(i)] - i ) return false; } return true; } auto solve8qreen () { std::vector<std::vector<int>> results {}; std::vector<int> choose {-1}; while (!choose.empty()) { choose.back()++; if (choose.back() == 8) { choose.pop_back(); continue; } if (!check(choose)) continue; if (choose.size() == 8) results.push_back(choose); else choose.push_back(-1); } return results; } int main () { auto soluctions {solve8qreen()}; int tests {}; std::cin >> tests; while (tests--) { std::vector<std::vector<int>> board (8, std::vector<int>(8)); for (auto& row : board) for (auto& grid : row) std::cin >> grid; int max_sum {-1}; for (const auto& soluction : soluctions) { int sum {0}; for (int i {0}; i < 8; i++) sum += board[u(i)][u(soluction[u(i)])]; if (sum > max_sum) max_sum = sum; } std::cout << std::setw(5) << max_sum << '\n'; } return 0; } ``` ### a090: 質數環 好看什麼的之後再說。 ```cpp= #include <algorithm> #include <iostream> #include <vector> constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } bool has (const std::vector<int>& array, int element) { return std::find(array.begin(), array.end(), element) != array.end(); } auto generatePrimes (int max) { std::vector<int> primes {2}; const auto isPrime {[&primes](int x) { for (const auto& prime : primes) { if (x % prime == 0) return false; } return true; }}; while (primes.back() < max) { int x {primes.back() + 1}; while (!isPrime(x)) x++; primes.push_back(x); } return primes; } auto generatePairs (int max) { std::vector<int> primes {generatePrimes(max * 2 - 1)}; std::vector<std::vector<int>> pairs(u(max)); for (int i {1}; i <= max; i += 2) { for (int j {2}; j <= max; j += 2) { if (!has(primes, i + j)) continue; pairs[u(i - 1)].push_back(j); pairs[u(j - 1)].push_back(i); } } return pairs; } int main () { const auto pairs {generatePairs(16)}; int cases {1}; int length {}; while (std::cin >> length) { std::vector<std::vector<int>> answers {}; std::vector<int> choose {-1}; std::vector<int> numbers {1}; while (!choose.empty()) { int next {++choose.back()}; auto& list {pairs[u(numbers.back() - 1)]}; if (numbers.size() == length) { if (has(pairs[0], numbers.back())) answers.push_back(numbers); } if (numbers.size() == length || next >= list.size() || list[u(next)] > length) { choose.pop_back(); numbers.pop_back(); continue; }; if (has(numbers, list[u(next)])) continue; numbers.push_back(list[u(next)]); choose.push_back(-1); } std::cout << "Case " << cases << ":\n"; for (const auto& answer : answers) { for (const auto& number : answer) std::cout << number << ' '; std::cout << '\n'; } std::cout << '\n'; cases++; } return 0; } ``` ### a286: 23 乘法的部分還是剪的,只是要倒過來算。 ```cpp= #include <bitset> #include <iostream> #include <vector> bool test (int level, int x, const std::bitset<5>& used, const std::vector<int>& numbers) { if (level == 5) return x == 0; for (std::size_t i {0}; i < 5; i++) { if (used[i]) continue; const std::bitset<5> new_used {used | std::bitset<5>(1 << i)}; if (level < 4 && x % numbers[i] == 0 && test(level + 1, x / numbers[i], new_used, numbers)) return true; if (level < 4 && test(level + 1, x + numbers[i], new_used, numbers)) return true; if (test(level + 1, x - numbers[i], new_used, numbers)) return true; } return false; } int main() { while (true) { std::vector<int> numbers(5); for (auto& n : numbers) std::cin >> n; if (numbers[0] == 0) return 0; if (test(0, 23, std::bitset<5>{}, numbers)) std::cout << "Possible\n"; else std::cout << "Impossible\n"; } } ``` ### a142: 無刻度容器倒水問題 這題是圖論之廣度優先搜尋。 只有兩杯那倒法就不用迴圈寫法了。 ```cpp= #include <algorithm> #include <iostream> #include <queue> #include <set> #include <utility> int soluction () { using pii = std::pair<int, int>; int a_limit {}, b_limit {}, target {}; std::cin >> a_limit >> b_limit >> target; std::queue<pii> visiting {}, to_visit {}; visiting.emplace(0, 0); std::set<pii> visited {{0, 0}}; const auto visit {[&visited, &to_visit, target] (pii state) { if (state.first == target || state.second == target) return true; if (visited.count(state)) return false; to_visit.push(state); visited.insert(state); return false; }}; int depth {1}; while (!visiting.empty()) { pii now {visiting.front()}; visiting.pop(); const int sum {now.first + now.second}; if ( visit({now.first, 0}) || visit({0, now.second}) || visit({now.first, b_limit}) || visit({a_limit, now.second}) || visit({std::max(sum - b_limit, 0), std::min(sum, b_limit)}) || visit({std::min(sum, a_limit), std::max(sum - a_limit, 0)}) ) return depth; if (!visiting.empty()) continue; std::swap(visiting, to_visit); depth++; } return -1; } int main () { int tests {}; std::cin >> tests; while (tests--) std::cout << soluction() << '\n'; return 0; } ``` ## 15. [基礎資料結構 II 及基礎演算法 II ] 動態規劃 ### a098: 計算方法數 法一: ```cpp= #include <iostream> #include <numeric> #include <vector> constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } int main () { const std::vector<int> coins {1, 5, 10, 25, 50}; // dp[sum][last use] = method types std::vector<std::vector<long long>> dp {{1}}; int target {}; while (std::cin >> target) { for (int i {static_cast<int>(dp.size())}; i <= target; i++) { dp.emplace_back(coins.size(), 0); for (int j {0}; j < coins.size(); j++) { if (coins[u(j)] == i) dp[i][j] = 1; if (coins[u(j)] >= i) break; for (int k {0}; k <= j; k++) dp[u(i)][u(j)] += dp[u(i - coins[u(j)])][u(k)]; } } const long long answer {std::accumulate(dp[u(target)].begin(), dp[u(target)].end(), 0LL)}; if (answer == 1) { std::cout << "There is only 1 way to produce "; } else { std::cout << "There are " << answer << " ways to produce "; } std::cout << target << " cents change.\n"; } return 0; } ``` 法二: ```cpp= #include <iostream> #include <vector> constexpr std::size_t u (int i) { return static_cast<std::size_t>(i); } int main () { std::vector<long long> methods(30000 + 1, 0); methods[0] = 1; for (const int coin : {1, 5, 10, 25, 50}) { for (int i {coin}; i <= 30000; i++) { methods[u(i)] += methods[u(i - coin)]; } } int target {}; while (std::cin >> target) { if (methods[u(target)] == 1) { std::cout << "There is only 1 way to produce "; } else { std::cout << "There are " << methods[u(target)] << " ways to produce "; } std::cout << target << " cents change.\n"; } return 0; } ``` ### a155: 雙子星塔 ```cpp= #include <algorithm> #include <iostream> #include <vector> int main () { for (int test {1}; ; test++) { std::size_t a_hight {}, b_hight {}; std::cin >> a_hight >> b_hight; if (!a_hight) return 0; std::vector<int> a_tower(a_hight); for (auto& brick : a_tower) std::cin >> brick; std::vector<int> b_tower(b_hight); for (auto& brick : b_tower) std::cin >> brick; std::vector<int> dp1(b_hight, 0), dp2(b_hight, 0); for (std::size_t i {0}; i < a_hight; i++) { for (std::size_t j {0}; j < b_hight; j++) { dp2[j] = std::max({ dp1[j], j == 0 ? 0 : dp2[j - 1], (j == 0 ? 0 :dp1[j - 1]) + (a_tower[i] == b_tower[j]) }); } std::swap(dp1, dp2); } std::cout << "Twin Towers #" << test << "\nNumber of Tiles : " << dp1.back() << "\n\n"; } } ``` ### a144: 農作物採收問題 ```cpp= #include <algorithm> #include <iostream> #include <vector> int main () { int width {}; std::cin >> width; std::vector<std::vector<int>> field(width, {0}); for (auto& row : field) { for (int i {0}; i < width; i++) { int block {}; std::cin >> block; row.push_back(row.back() + block); } } int max {0}; for (std::size_t from {0}; from < width; from++) { for (std::size_t to {from}; to < width; to++) { int sum {0}; for (const auto& row : field) { sum = std::max(sum + row[to + 1] - row[from], 0); max = std::max(max, sum); } } } std::cout << max << '\n'; } ``` ### a101: 分銅幣 實在想不到,只好作弊用位元運算了。 ```cpp= #include <bitset> #include <cmath> #include <iostream> #include <set> int main () { constexpr std::size_t maxdiff {500 * 100}; int tests {}; std::cin >> tests; while (tests--) { std::bitset<maxdiff> diffs {0b1}; int coins {}; std::cin >> coins; while (coins--) { unsigned int coin {}; std::cin >> coin; std::bitset<maxdiff> diffs_new {(diffs << coin) | (diffs >> coin)}; for (std::size_t i {0}; i < coin; i++) { if (!diffs[i]) continue; diffs_new[coin - i] = true; } diffs = std::move(diffs_new); } for (std::size_t i {0}; i < maxdiff; i++) { if (!diffs[i]) continue; std::cout << i << '\n'; break; } } return 0; } ``` ### a143: 關鍵字搜尋模擬 ```cpp= #include <algorithm> #include <cmath> #include <iostream> #include <sstream> #include <stdexcept> #include <string> #include <utility> #include <vector> float getScore (const std::string& a, const std::string& b) { int max {0}; std::vector<int> dp(b.size(), 0); for (std::size_t i {0}; i < a.size(); i++) { for (std::size_t j {b.size() - 1}; j != -1; j--) { if (a[i] == b[j]) dp[j] = (j == 0 ? 0 : dp[j - 1]) + 1; else dp[j] = 0; if (dp[j] > max) max = dp[j]; } } return static_cast<float>(max) / a.size(); } int main() { std::vector<std::string> searchs {}; std::string input {}; std::getline(std::cin, input); { std::stringstream sstream {input}; while (sstream >> input) searchs.push_back(input); } int files_num {}; std::cin >> files_num; std::vector<std::pair<int, int>> files {}; bool flag {false}; while (files_num--) { int id {}; std::cin >> id; std::getline(std::cin, input); std::stringstream sstream {input}; float scoreSum {}; while (sstream >> input) { float score {}; for (const auto& searchkeyword : searchs) score = std::max(score, getScore(searchkeyword, input)); scoreSum += score; } if (scoreSum > 0) flag = true; files.emplace_back(id, std::round(scoreSum * 100)); } std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) { return a.second == b.second ? a.first < b.first : a.second > b.second; }); if (!flag) { std::cout << "FALSE\n"; return 0; } for (const auto& file : files) std::cout << file.first << ' '; std::cout << '\n'; return 0; } ``` ### a145: 搬家規畫問題 ```cpp= #include <algorithm> #include <iostream> #include <sstream> #include <string> #include <vector> int main () { std::vector<std::pair<int, int>> items {}; { std::string input {}; std::getline(std::cin, input); std::stringstream sstream {input}; int weight {}; while (sstream >> weight) items.emplace_back(weight, 0); } for (auto& item : items) std::cin >> item.second; int max_weight {}; std::cin >> max_weight; std::vector<int> max_values(max_weight + 1, 0); for (const auto& item : items) { for (std::size_t i {max_weight}; i >= item.first && i != -1; i--) { max_values[i] = std::max(max_values[i], max_values[i - item.first] + item.second); } } std::cout << max_values.back() << '\n'; } ``` ## 16. [基礎資料結構 II 及基礎演算法 II ] 樹狀/圖形結構演算法 ### a136: 元件測試排程問題 ```cpp= #include <algorithm> #include <iostream> #include <queue> #include <utility> #include <vector> int main() { int items_num{}, needs_num{}; std::cin >> items_num >> needs_num; std::vector<int> items(items_num); std::vector<std::pair<int, int>> edges(needs_num); for (auto& edge : edges) { char first{}, second{}; std::cin >> first >> second; edge = { first - 'A', second - 'A' }; items[edge.second]++; } std::queue<int> to_visit{}; std::vector<int> sorted{}; bool noanswer{ false }; int lastuse{ -1 }; for (int i{ 0 }; i < items_num; i++) { if (items[i] == 0) to_visit.push(i); } while (sorted.size() < items_num) { if (to_visit.empty()) { const auto& edge = edges.back(); edges.pop_back(); if (items[edge.first] == 0) continue; if (--items[edge.second] > 0) continue; to_visit.push(edge.second); } while (!to_visit.empty()) { const int now{ to_visit.front() }; to_visit.pop(); if (!noanswer && !to_visit.empty()) noanswer = true; sorted.push_back(now); for (const auto& edge : edges) { if (edge.first != now) continue; if (--items[edge.second] != 0) continue; to_visit.push(edge.second); if (edges.size() != needs_num) continue; lastuse = std::max(lastuse, static_cast<int>(std::find(edges.begin(), edges.end(), edge) - edges.begin())); } } } if (edges.size() != needs_num) std::cout << "Order conflict after getting pair " << edges.size() + 1 << '\n'; else if (noanswer) std::cout << "No answer\n"; else { std::cout << "Determine the testing sequence after getting pair " << lastuse + 1 << " : "; for (const auto& item : sorted) std::cout << static_cast<char>('A' + item); std::cout << '\n'; } return 0; } ``` ### a137: 勇者冒險 ```cpp= #include <algorithm> #include <iostream> #include <queue> #include <utility> #include <vector> int main() { using pos_t = std::pair<int, int>; int rows {}, columns {}; std::cin >> rows >> columns; pos_t start {}, end {}; std::cin >> start.first >> start.second >> end.first >> end.second; std::vector<std::vector<int>> map (rows, std::vector<int>(columns, -1)); map[end.first][end.second] = 0; int blocks_num {}; std::cin >> blocks_num; while (blocks_num--) { pos_t pos {}; std::cin >> pos.first >> pos.second; std::cin >> map[pos.first][pos.second]; } std::vector<std::vector<int>> min_levels (rows + 1, std::vector<int>(columns + 1, -1)); min_levels[start.first][start.second] = 0; const auto comp = [&min_levels](pos_t a, pos_t b) { const int a_distance {min_levels[a.first][a.second]}; const int b_distance {min_levels[b.first][b.second]}; return a_distance > b_distance; }; std::priority_queue<pos_t, std::vector<pos_t>, decltype(comp)> to_visit {comp}; to_visit.push(start); while (!to_visit.empty()) { const pos_t pos {to_visit.top()}; to_visit.pop(); const int base_level {min_levels[pos.first][pos.second]}; pos_t dpos {0, 1}; for (int i {0}; i < 4; i++) { dpos = {dpos.second, -dpos.first}; const pos_t new_pos {pos.first + dpos.first, pos.second + dpos.second}; if (new_pos.first < 0 || new_pos.first >= rows || new_pos.second < 0 || new_pos.second >= columns) continue; const auto level {map[new_pos.first][new_pos.second]}; if (level == -1) continue; const int come_level {std::max(level, base_level)}; auto& min_level {min_levels[new_pos.first][new_pos.second]}; if (min_level != -1 && come_level >= min_level) continue; min_level = come_level; to_visit.push(new_pos); } } std::cout << min_levels[end.first][end.second] << '\n'; return 0; } ``` ### a138: 最小花費的航空之旅 ```cpp= #include <algorithm> #include <iostream> #include <queue> #include <utility> #include <vector> #include <map> struct State { int cost {}; int city {}; std::vector<int>::iterator complete {}; std::vector<int> used {}; bool is_used (int ticket_id) const { return std::find(used.begin(), used.end(), ticket_id) != used.end(); } bool operator< (const State& other) const { return cost > other.cost; } }; struct Ticket { int id {}; int cost {}; std::vector<int> path {}; }; int main() { int tickets_num {}; std::cin >> tickets_num; std::map<int, std::vector<Ticket>> tickets {}; for (int i {1}; i <= tickets_num; i++) { Ticket ticket {i}; std::cin >> ticket.cost; int length {}, start {}; std::cin >> length >> start; ticket.path.resize(length - 1); for (auto& city : ticket.path) std::cin >> city; tickets[start].push_back(ticket); } int route_length {}; std::cin >> route_length; std::vector<int> route(route_length); for (auto& city : route) std::cin >> city; std::priority_queue<State> states {}; states.push({0, route.front(), ++route.begin()}); while (states.top().complete != route.end()) { const State state {states.top()}; states.pop(); for (const auto& ticket : tickets[state.city]) { if (state.is_used(ticket.id)) continue; State new_state {state}; new_state.used.push_back(ticket.id); new_state.cost += ticket.cost; for (const auto& city : ticket.path) { if (new_state.complete == route.end()) break; if (city == *new_state.complete) new_state.complete++; new_state.city = city; states.push(new_state); } } } std::cout << "Cost = " << states.top().cost << ", Tickets used: "; bool first {true}; for (const auto& ticket_id : states.top().used) { if (!first) std::cout << ", "; std::cout << ticket_id; if (first) first = false; } std::cout << "\n"; return 0; } ``` ## 17. [題目解析] APCS 程式開發環境、題目解析 ### a160: 線段覆蓋長度 ```cpp= #include <algorithm> #include <iostream> #include <utility> #include <vector> int main () { int lines {}; std::cin >> lines; std::vector<std::pair<int, int>> points {}; while (lines--) { int start {}, end {}; std::cin >> start >> end; points.emplace_back(start, 1); points.emplace_back(end, -1); } std::sort(points.begin(), points.end()); int lastpos {}; int length {0}; int lines_num {0}; for (const auto& point : points) { if (lines_num > 0) length += point.first - lastpos; lines_num += point.second; lastpos = point.first; } std::cout << length << '\n'; return 0; } ``` ### a140: 物品堆疊 ```cpp= #include <algorithm> #include <iostream> #include <vector> #include <utility> int main () { int items_num {}; std::cin >> items_num; std::vector<std::pair<int, int>> items(items_num); for (auto& item : items) std::cin >> item.first; for (auto& item : items) std::cin >> item.second; std::sort(items.begin(), items.end(), [](const auto& a, const auto& b) { return a.first * b.second < b.first * a.second; }); long long sumw {0}; long long sumcost {0}; for (const auto& item : items) { sumcost += sumw * item.second; sumw += item.first; } std::cout << sumcost << '\n'; return 0; } ```