--- tags: Academic --- # 題解:紫荊組 [Final Scoreboard](https://i.imgur.com/fnMpyVz.jpg) ## 說明 by 黃鈺程、曾俊宏 有鑒於不少隊伍於比賽過程對於比賽模式進行提問,在此補充說明一下程式競賽是怎麼進行的。 1. 隊伍將程式碼 submit 後,裁判這邊**並不是**手動輸入幾筆測資看程式碼有沒有正確輸出。裁判這邊是透過程式,將預先產生的好的測資餵進你的程式,我們一般情況下不會去手動跑你的程式。所以程式碼中並不用寫 `printf("請輸出測資")` 之類的東西,這反而會造成你的程式碼被判定為 Wrong Answer。 2. 測資餵進你的程式時,表現得像是有人在手動輸入一樣,所以你只須從標準輸入讀資料(即使用 `scanf` 或 `cin`),並不需要自行開檔,然後從檔案讀入。 3. 餵進你的程式的測資通常會跟題目中給的範例輸入不同(但當然會符合題目中的技術規格與輸入格式),測資中也通常會包含非常強的測資,即數字會是技術規格中所說的上限) 4. 當讀進資料後,你的程式應該符合輸出格式地輸出答案然後停止。一般來說每筆測資輸出一行,最後一筆測資後也是如同其他測資要換行。 5. 答案不用等到全部讀完才一起輸出,也就是說你在輸入很多比測資的時候,算好就可以輸出答案了! 6. 如果你的程式能在題目所說的時限內輸出答案,那裁判這邊的程式會比對你的輸出與正確答案,來確定你是 Correct 還是 Wrong Answer 或其他。 7. 其他常見的結果為:Time Limit Exceeded(你的程式跑到題目規定的時間到了還沒終止)、Runtime Error(你的程式跑一跑 crash 了,通常是程式碼中有 bug)、Presentation Error(你的程式輸出多餘的東西,不符合題目的輸出格式)、Compilation Error(你傳上來的程式碼根本無法編譯…請先在你的電腦編一下,確定可以再傳上來)。 以這次比賽來說,除了第一題與第五題以外,其他題都可以使用以下程式碼架構: C: ```c= #include <stdio.h> int main() { int testcase; scanf("%d", &testcases); while (testcases--) { // 總共會跑 testcases 次 // 讀入測資 // 運算 printf("%d\n", ans); // 輸出答案,記得換行 } return 0; // 記得加,不加有時會被造成問題 } ``` C++: ```cpp= #include <iostream> using namespace std; int main() { int testcases; cin >> testcases; while (testcases--) { // 總共會跑 testcases 次 // 讀入測資 // 運算 cout << ans << endl; // 輸出答案,記得換行 } return 0; } ``` Python 中 `input()` 是整行輸入,而 `print` 預設會自動加上換行: ```python= def solve(): # 讀入測資 # 運算 print(ans) TC = int(input()) for _ in range(TC): # 執行 TC 次 solve() ``` 另外,在使用 C++ 讀入時要小心,`cin` 會根據你變數的型態來決定他要怎麼讀,如果假設輸入是: ``` 123.456 ``` 是浮點數,如果你使用 ```cpp int a; cin >> a; // a = 123 ``` 那 `a` 會是 `123` 而不是你預期的 `123.456`。正確的寫法是: ```cpp float a; cin >> a; // a = 123.456 ``` ## 道歉啟示 對於 房地產投資客 和 K進制轉換 兩題的題目中出現的錯誤,我們均是在比賽進行中和題解撰寫時才發現。對於這些錯誤,我們會銘記在心,並會在下一屆比賽時特別注意。 ## A. 多少錢 題目:教授 題解:黃鈺程 讀入 4 個正整數,輸出結果。 [C++ by 曾俊宏](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E5%A4%9A%E5%B0%91%E9%8C%A2/henry.cpp) ## B. 金錢規劃 題目:教授 題解:曾俊宏 照著題目做即可! 先讀取總測資數後,對於每一筆測資讀兩數字整數(分別是收入 $I$ 與支出筆數 $N$),之後再讀取 $N$ 個數字並且從 $I$ 中扣除即可! [Python by 黃鈺程](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%87%91%E9%8C%A2%E8%A6%8F%E5%8A%83/amos.py) [C++ by 郭晏誌](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%87%91%E9%8C%A2%E8%A6%8F%E5%8A%83/macaca.cpp) ## C. 清償卡債 題目:教授 題解:黃鈺程 照著題目做~記得使用 `float` 或 `double` 來存有小數點的變數。在計算 30 次方時,你可以自己寫個迴圈,也可以使用 C/C++ 在 `math.h`/`cmath` 下內建的 `pow`。 [Python by 黃鈺程](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E6%B8%85%E5%84%9F%E5%8D%A1%E5%82%B5/amos.py) [C++ by 陳煒杰](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E6%B8%85%E5%84%9F%E5%8D%A1%E5%82%B5/andy.cpp) [C++ by 陳煒杰, pow](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E6%B8%85%E5%84%9F%E5%8D%A1%E5%82%B5/andyforpow.cpp) ## D. 房地產投資客 題目:教授 題解:黃鈺程 非常抱歉,這題題目有一點小瑕疪,我們現在寫題解時才注意到:題目關於「投入金額、房貸利率、操作次數」的型態,輸入格式、技術規格、範測是不太一致的。我們測試的測資裡使用的是技術規格中所說的 float, float, int。這可能造成部份隊伍只使用範測測試是找不出這種情況的。 另外這題在 judge 時有開放浮點數誤差,只要你的結果與答案誤差在 0.1 以下即可。比賽中會 rejudge(沒有影響到紫荊組) 來自於 pc^2 會錯誤估計程式執行時間,他把他判斷是否在誤差內的時間也算進去,而他判斷的時間莫名的長……。至於有些隊得到的 Incomplete Output,是因為在 pc^2 的誤差處理機制有瑕疵。如果最後一筆測資在輸出答案的時候並沒有輸出換行,會造成比對錯誤。 另外比賽中有許多隊沒看到要輸出結果至**小數點後二位**,造成無法 AC。 Python(3.6) 的寫法是 `print(f'{ans:.2f})'` C 的寫法是 `printf(“%.2f“, ans);` C++ 的寫法是 `cout << fixed << setprecision(2) << ans << endl;` [Python by 黃鈺程](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E6%88%BF%E5%9C%B0%E7%94%A2%E6%8A%95%E8%B3%87%E5%AE%A2/amos.py) [C++ by 陳煒杰](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E6%88%BF%E5%9C%B0%E7%94%A2%E6%8A%95%E8%B3%87%E5%AE%A2/andy.cpp) ## E. 工讀金 題目:教授 題解:黃鈺程 這題是考你 IO。這種不知道要讀多少,只知道讀到什麼才結束的情況,可以使用無窮迴圈來讀(我知道許多老師不建議使用,不過總是難免的,當然你可以引入一些指示用的變數來讓可讀性增加),你可以這樣寫成: ```cpp while (true) { int x; cin >> x; // or scanf(" %d", &x) if (x == 0) { break; } } ``` 如果你迴圈中的第一行一定是讀入變數,那你可以寫成 ```cpp int x; while (cin >> x && x != 0) { // or while (scanf(" %d", &x) && x != 0) // ... } ``` 於是這題就輕鬆解決啦~ [Python by 黃鈺程](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E5%B7%A5%E8%AE%80%E9%87%91/amos.py) [C++ by 郭晏誌](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E5%B7%A5%E8%AE%80%E9%87%91/macaca.cpp) [C++ by 陳煒杰](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E5%B7%A5%E8%AE%80%E9%87%91/andy.cpp) ## F. 計算矩形數量 題目:教授 題解:郭晏誌、黃鈺程 這題因為 N 很小,所以我們可以用暴力的方法解:枚舉所有可能的矩形位置。我們用 2 個 for 枚舉矩形的左上角位置,2 個 for 枚舉矩形的右下角位置,之後我們檢查這個矩形的四個角是不是都是 1。整體時間是 $O(N^4)$。 數學一點的解法就是矩陣列運算如果是矩形的話對應位置的 1 會被扣成 0, 所以矩陣的列相減之後 seqential 檢查是否有原本是 1 的位置相減之後變成 0。 以第一行往下對第二第三行做列運算: 1.第二行的第一個 1 與最後一個 1 被扣成 0,這就告訴我們它可以湊出一個矩形。 2.第三行的第三個 1 與最後一個 1 被扣成 0,這就告訴我們它可以湊出一個矩形。 1 0 1 0 0 1 1 0 1 0 0 1 1 0 0 0 0 1 -> 0 0 -1 0 0 0 0 0 1 0 0 1 -1 0 0 0 0 0 [Python by 黃鈺程](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E8%A8%88%E7%AE%97%E7%9F%A9%E5%BD%A2%E6%95%B8%E9%87%8F/amos.py) [C++ by 曾俊宏](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E8%A8%88%E7%AE%97%E7%9F%A9%E5%BD%A2%E6%95%B8%E9%87%8F/henry.cpp) ## G. K 進制數字 題目:教授 題解:曾俊宏 再次抱歉,這題題目我們 review 時沒有發現 51 被打成 52。 這題最核心的最法是使用長除法! 以十進位的 51 換成三進位表示法的話,過程如下: 1. 51 / 3 商 17 餘 0 2. 17 / 3 商 5 餘 2 3. 5 / 3 商 1 餘 2 4. 1 / 3 商 0 餘 1 所以,我們可以得到 $(51)_{10}$ 換底之後的答案就是 $(1220)_2$ 啦! 但是這一題有兩個坑,要看你怎麼實作,才知道會不會遇到。 ### 坑一 $N = 0$,如果你是類似這樣寫 ``` int ans[100]; int i = 0 while(N > 0) { ans[i] = N % k; N /= K; i++; } ``` 想想看 0 的 case 你的 output 會是什麼? ### 坑二 如果你把答案記錄在int裡面,我們看看下面這一組測資: 十進位 $9123$ 換成二進位會是 $10001110100011$,這個答案會不會超過 c/c++/java的 int 可以存的範圍呢? 會喔~ [C++ by 李晨維](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%80%B2%E5%88%B6%E8%BD%89%E6%8F%9B/tom.cpp) [C++ by 曾俊宏](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%80%B2%E5%88%B6%E8%BD%89%E6%8F%9B/henry.cpp) ## H. 單字背誦 題目:黃鈺程、郭晏誌 題解:黃鈺程 這題是經典的一筆畫問題。用圖論的語言來講,是尤拉環(Euler Circuit)。每筆測資可以轉換成一個有向圖 $G$。每個單字是圖中的邊,圖的節點是字母,例如 `[electricity, yarn, negotiate, abandon, ninja]` 與 `[xy, yx, ab, ba]` 分別建成圖: ![Google 繪圖很好用的](https://i.imgur.com/TRcUdrC.png) 題目所求就是在問 $G$ 是不是一個或多個歐拉環所組成,而這可以藉由檢查圖中是不是每個節點的入邊數(in degree)等於出邊數(out degree)。這個 **充要關係** 可在各離散課本找到,因此不在此說明。題外話,如果這題改成問給定的單字可不可以組成 **僅一個** 歐拉環,那就得先判這個圖中所有 degree ≠ 0 的點是連通的喔。 不過如果不知道尤拉環,這題也是某種程度可以解出來的: 1. 每個單字只有字首與字尾的字母有意義,中間有什麼字母不影響結果。 2. 如果一筆測資是 Lucky,則對於任意字母,它「出現在單字字首的次數」要等於「出現在單字字尾的字數」。 這可以用反證法證明:假設這些單字是 Lucky,且字母 c 出現在字尾的次數比出在字首的次數多一,則會有一個字尾為 c 的單字找不到下一個單子繼續接,因此不是 Lucky。 至此,你就可以 **猜說** 是不是統計一下各字母的情況,來判定是 Lucky 還是 QAQ。這剛好會是對的,不過不正確,因為並沒有在邏輯中證明這是「若且唯若」的關係。上述只證明了 => 的關係,並沒有 <=,也就是說如果統計的結果成立,那這筆測資就一定是 Lucky 嗎?(答:是的,性質由歐拉環的證明保證)。 我不知道解出這題的選手們是不是都有注意到這個,不過就算沒有,既然是比賽,證明不嚴謹是可以接受的啦~有想法比較重要。這題被許多應該沒聽過歐拉環的人(非資工系的選手與資工大一)解出來,讓我非常驚訝! 這題實作非常簡單,統計一下即可,利用英文字母的 ASCII Code 是連續的,可以直接開陣列算一下各字母的次數。 [Python by 黃鈺程](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E5%96%AE%E5%AD%97%E8%83%8C%E8%AA%A6/solution.py) [C++ by 郭晏誌](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E5%96%AE%E5%AD%97%E8%83%8C%E8%AA%A6/macaca.cpp) ## I. 選課 題目:曾俊宏、黃鈺程、陳煒杰 題解:曾俊宏 這題其實在冗長的題目敘述背後,只是要求取不超過 $K$ 的重疊總長下,的最大的區間覆蓋長度值。 由於這一題的 $N$ 只有 $18$,所以我們可以對於所有的可能區間組合進行枚舉,並對於每次的枚舉組合進行重疊總長度的計算。只要重疊總長度不高過 $K$, 我們就嘗試更新最大值。 實作部分,可以使用二進制數字的性質進行爆搜,也可以使用 DFS 來進行爆搜。 [C++ 二進制爆搜 by 曾俊宏](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%81%B8%E8%AA%B2/solution.cpp) [C++ DFS爆搜 by 曾俊宏](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%81%B8%E8%AA%B2/solution2.cpp) [Java 二進制爆搜 by 曾俊宏](https://github.com/henrybear327/2018-CCU-Pineapple-Contest/blob/master/%E9%81%B8%E8%AA%B2/Main.java)