# 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;
}
```