---
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]` 分別建成圖:

題目所求就是在問 $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)