# TOI 2021下學期 新手組題目解析 ## 3月 ### 1. [時區(Zone)](https://zerojudge.tw/ShowProblem?problemid=f706) #### 題目分析 * 時間系統跟現實的異同 * 時、分、秒數量關係不變。換句話說,$1$ hr = $60$ mins,$1$ mins = $60$ secs * 一天還是$24$個時區 * 一個時區變成$1.5$小時,因此一天有$36$小時,也就是$36 \times 60= 2160$ 分鐘 * 加減時區$T$會影響到... * 秒數 $S$ :不變 * 分鐘數 $M$ :雙數個時區不變,單數個時區會加減$30$。結果可能大於60或小於$0$,因此必須是除以$60$的餘數。另外,超過$60$則多一小時,小於$0$則少一小時。 * 小時數 $H$ :有可能跨過換日線。超過36會多一天,小於0會少一天,因此必須是除以$36$的餘數。 #### 解題技巧說明 * 調整 $T$ :往前 $n$ 個時區,相當於往後 $(24-n)$ 個時區。因此,時區可以統一往前移,亦即把 $T$ 統一為正數。 * 小時全部化為分鐘:分鐘數依時區加減完畢後,再除以 $60$ ,商數即 $H$ ,餘數即 $M$ #### 注意事項 * 負數除法結果。e.g. $(-5) \div 2$ * 數學運算結果:商為$3$,餘數為$1$ * 程式運算結果:商為$2$,餘數為$-1$ * 輸出格式。 * $H$、$M$、$S$ 均為正數 * $M$、$S$ 若小於10,需補0,湊滿兩位數 * $H$ 不用動 #### 測資發想 題目測資只有部分狀況,請考量各種極端狀況,自己生測資來測試。 * 各種排列組合: * $T$ 為正、$T$ 為負 * $M>30$ 的時候加單數時區、$M<30$的時候減單數時區 * 結果大於$36:00:00$、小於$0:00:00$ #### 範例程式碼 ::: spoiler 調整T ```cpp= #include <iostream> using namespace std; int main() { int h, m, s, t; cin >> h >> m >> s >> t; if (t < 0) { t = t + 24; } m += t * 90 % 60; h += t * 90 / 60; // 因為t必為正,僅需考慮m超過60、h超過36的狀況來調整m, h if (m > 60) { m = m - 60; h = h + 1; } if (h >= 36) { h = h - 36; } cout << h << ":"; if(m<10) cout<<"0"; cout << m << ":"; if(s<10) cout<<"0"; cout << s << "\n"; return 0; } ``` ::: ::: spoiler 化小時為分鐘 ```cpp= #include <iostream> using namespace std; int main() { int h, m, s, t; cin >> h >> m >> s >> t; int msum = h*60 + m + t*90; if (msum < 0) msum += 2160; m = msum % 60; h = (msum/60) % 36; cout << h << ":"; if(m<10) cout<<"0"; cout << m << ":"; if(s<10) cout<<"0"; cout << s << "\n"; } ``` ::: ::: spoiler 調整T、化小時為分鐘 ```cpp= #include <iostream> using namespace std; int main() { int h, m, s, t; cin >> h >> m >> s >> t; if(t < 0) t = t+24; int msum = h*60 + m + t*90; m = msum % 60; h = (msum/60) % 36; cout << h << ":"; if(m<10) cout<<"0"; cout << m << ":"; if(s<10) cout<<"0"; cout << s << "\n"; } ``` ::: --- ### 2. [幸運7(Lucky 7)](https://zerojudge.tw/ShowProblem?problemid=f707) #### 題目分析 * 輸入:以0為終點,不確定會有幾個數字,已知最多有10個數字 * 比較:一次抽兩個數字出來比較,分成四種情況,再做對應的處理: * 兩數皆可被7整除:取70的餘數較大的比較大 * 第一數可被7整除:第一數較大 * 第二數可被7整除:第二數較大 * 兩數皆無法被7整除:取 77 的餘數較小的比較大 #### 解題技巧說明 * 使用for迴圈或while迴圈進行輸入 * 使用多重選擇結構進行判斷 * 資料結構 * 不使用資料結構:輸入一個新的數字,就要馬上與前一數比較,找出最大值,重複直到最後一個數字。 * 使用資料結構(如:陣列):因無法預估長度,可預先開固定大小的陣列。 #### 注意事項 * 使用if-else if-else時,注意先後順序 * 如不使用資料結構,在輸入下一個數字之前,就要立刻進行比較 * 如果使用陣列,陣列大小應該開11。之所以是11,是因為剛好有10個數字的話,會存到最後一個0。 * 如果使用陣列,全部開始比較之前,需要設一暫存變數存「目前為止的最大值」,而暫存變數的值建議設為0或第0個數字的值。 #### 測資發想 * 各種排列組合: * 四種情況 * 兩個數字,或兩個以上的數字 * 最大數出現在第一個、中間、最後一個 #### 範例程式碼 ::: spoiler 使用while,不使用迴圈 ```cpp= #include <iostream> using namespace std; int main() { int n; int max; cin >> n; max = 0; while(n != 0){ if (max == 0) max = n; else{ if(n%7 == 0 && max%7 != 0) max = n; else if(n%7 == 0 && max%7 == 0){ if(n%70 > max%70) max = n; }else if(n%7 != 0 && max%7 != 0){ if(n%77 < max%77) max = n; }else{ ; } } cin >> n; } cout << max << endl; return 0; } ``` ::: ::: spoiler 使用for-loop搭配break/continue指令,陣列開最大 ```cpp= #include <iostream> using namespace std; // 已知全部數字加上0,不會超過11個數字 #define N 11 int main() { int n, max = 0; for(int i = 0; i < N; i++){ cin >> n; if (n == 0) break; if (max == 0){ max = n; continue; } if(n%7 == 0 && max%7 != 0) max = n; else if(n%7 == 0 && max%7 == 0){ if(n%70 > max%70) max = n; }else if(n%7 != 0 && max%7 != 0){ if(n%77 < max%77) max = n; }else{ ;// 展示用,這裡什麼都不用做,最大值不變 } } cout << max << endl; return 0; } ``` ::: --- ### 3. [蟲蟲危機(Insect)](https://zerojudge.tw/ShowProblem?problemid=f708) #### 題目分析 * 所有螞蟻的數量要比蚱蜢多:比較前面兩個輸入即可得知 * 螞蟻的總身高比蚱蜢高:用兩個變數和迴圈,分別連加求得螞蟻、蚱蜢總身高。 #### 注意事項 * 輸入一個數字後,可以馬上累加起來,和計算機的原理很像,因此不需要分成兩個迴圈去處理。 #### 測資發想 * 各種排列組合: * 螞蟻的數量大於、等於、小於蚱蜢的數量 * 螞蟻的總身高大於、等於、小於蚱蜢總身高 #### 範例程式碼 ```cpp= #include <iostream> using namespace std; int main() { int m, n, msum = 0, nsum = 0; cin >> m >> n; int mm[m], nn[n]; for(int i=0; i<m; i++){ cin >> mm[i]; msum += mm[i]; } for(int i=0; i<n; i++){ cin >> nn[i]; nsum += nn[i]; } if(m<=n) cout << "No\n"; else if(msum<=nsum) cout << "No\n"; else cout << "Yes\n"; return 0; } ```