# 111學年度初階班上學期期末考題解 這次考試我出的有一點上頭 所以有一點點難 但這裡面的題目其實花時間仔細想想 其實解法都不複雜 ## a884 sum [題目](http://203.64.191.163/ShowProblem?problemid=a884) 這題非常簡單, 就是輸入陣列加簡單的if判斷 ``` if(後項-前項 > 0){ ans += 後項-前項 } ``` 但是這題有一個小陷阱導致有一些人只有$50%$ 我題目寫所有側資保證$1 \leq a_i \leq 2147483647$ $2147483647$的確是$int$的範圍 但這題會有加法 所以經過加法後會超過$int$的範圍 所以要記得用$long long int$ --- ## a881 Only Wah Purposely [題目](http://203.64.191.163/ShowProblem?problemid=a881) 簡單數學 根據題目可以知道每次直播最少會 wah 2次,最多6次 那麼如果能夠整除6的話,直播次數就會是 n/6 ~ n/2 但如果不能整除6,多出來的那一次必須開新的直播 就算只有多一次也能讓上一個直播 wah 5次來達到最少直播數 而若是無法整除2,則不用加,因為直播無法只 wah 1次 整合一下就會變成 %6 == 0 : n/6 ~ n/2 else : n/6+1 ~ n/2 --- ## a878 好想海底撈____________________________月 [題目](http://203.64.191.163/ShowProblem?problemid=a878) 這題其實還蠻簡單的,只要先getline進去之後用str.find找空格,找到最後 一個之後輸出之後的就好。但這樣很累,所以有一個酷東東叫str.rfind 用法跟find一樣,只是它會從後面找回來,所以直接就是最後一個空格。 --- ## a853 秋葉原朝聖之旅 [題目](http://203.64.191.163/ShowProblem?problemid=a853) 先把第一個數字存起來 接著把剩下的數字用 while + vector.push_back 儲存起來 用內建 sort (或其他排序) 完之後便會由小大排列 之後再用原本的存款數字由 vector 頭開始往後減 中間要記錄減了多少次 如果減到一半變成負數則 break 迴圈 直接輸出記錄結果 但如果跳出迴圈後結果與 vector.size() 相符 表示有全部買齊,則輸出 "Hololive Daisuki !!!" --- ## a873 even&odd [題目](http://203.64.191.163/ShowProblem?problemid=a873) 這題沒多少人寫出來,但這題其實很簡單,他只是藏在有點後面 題目要求是分別排序奇數和偶數 其實只要開三個陣列就可以解決這個問題 一個存原本的數列 一個存奇數 一個存偶數 >奇數和偶數的陣列建議用vector 因為只要直接push_back就好 不用特別看陣列要開多大(用array開太大排序時會影響) 把存偶數和奇數的陣列分別排序 接下來輸出時 只要判原數列是偶數就輸出偶數陣列的 只要判原數列是奇數就輸出奇數陣列的 --- ## a877 迴文針 [題目](http://203.64.191.163/ShowProblem?problemid=a877) 除了最後一個測資其實都還好,有兩種做法 第一個是直接將字串反轉之後比較原本的跟反轉的是否相同 第二個是從字串頭跟尾分別比較是否相同,比到中間停 最後一個測資比較麻煩是因為有空格跟標點 可以先getline之後把標點一個一個判斷掉 但這樣程式會很長 所以有一個東西是isalnum() 這個可以直接判斷括弧內的值是否為數字或字母,是的話輸出1,反之為0 這樣就可以直接刪掉非字母數字的東西然後再判斷。 --- ## a870 正方形 [題目](http://203.64.191.163/ShowProblem?problemid=a870) 這題是一個小小的數學 那就是最大公因數和最小公倍數 ### 幾何意義 兩數當長方形的邊長,這時最大公因數就是可以填滿長方形的最大正方形邊長 兩數當長方形的邊長,這時最大公倍數就是長方形可以組成的最小正方形 ### 數學 最大公因數稱為GCD 最小公倍數稱為LCM $LCM(a,b) = (a \times b)/GCD(a,b)$ 這裡我就不證明了,想知道的自己去查 ### 輾轉相除法(歐幾里得演算法) 我們可以用輾轉相除法求出GCD 假設現在有一個長方形邊長為38和16 我們要找出可以填滿長方形的最大正方形邊長 我們可以用小邊構造出長16的正方形 再用這個正方形填入長方形 剩下的區域變成了一個新的長方形 新的長方形再繼續剛剛的流程直到只剩正方形 最後的正方形就是答案了 上面的想法可以簡化成數學式 $gcd(a,b)=gcd(b,a\%b),(a>b)$ ### 輾轉相除法虛擬碼 迴圈 ```= gcd(a,b): while 0 < b: r <- a % b a <- b b <- r return a ``` 遞迴(可以想想為什麼這次是回傳b,不像剛剛是a) ```= gcd(a,b): r <- a % b if r == 0: return b gcd(b,r) ``` --- ## a897 () [題目](http://203.64.191.163/ShowProblem?problemid=a879) 學過stack的應該覺得這題很簡單 這題其實就只要想像一下 假設題目是(()()) 我從最左邊的開始一個一個推入陣列 只要出現()就消掉 ( (( (()可以消$\implies$( (( (()可以消$\implies$( ()可以消$\implies$沒了 如果像現在這樣全部消完就代表這個括號是合理的 如過無法全部消掉就是不合理的 例如 ())(() ()消掉 ) )( )(( )(()消掉 )( 最後剩這樣就是不合理的括號 --- ## a875 最佳區間 [題目](http://203.64.191.163/ShowProblem?problemid=a875) 這題和a074非常像,可以去寫a074看看 這題如果單50%測資的話非常簡單 for迴圈直接炸開 但如果要100%的話就要用到一點點greedy的概念(沒聽過可以不用理他) 我們想像在數列中有兩個指針 這兩個指針的中間就是現在要取的範圍 我們直接舉例 6 8 3 0 4 -2 2 1 我們要找到一個最短的時間賺到8元以上 ```cpp= ↓ 3 0 4 -2 2 1 現在0元,0小時 ↑ 不夠所以上指針向右 ↓ 3 0 4 -2 2 1 現在3元,1小時 ↑ 不夠所以上指針向右 ↓ 3 0 4 -2 2 1 現在3元,2小時 ↑ 不夠所以上指針向右 ↓ 3 0 4 -2 2 1 現在7元,3小時 ↑ 不夠所以上指針向右 ↓ 3 0 4 -2 2 1 現在9元,5小時 ↑ 錢夠了,ans = min(ans,5) 如果5比現在的最短打工時間短就更新,長就保留原本的 現在錢超過了不用再排更長的班,所以下指針向右 ↓ 3 0 4 -2 2 1 現在6元,4小時 ↑ 不夠所以上指針向右 ↓ 3 0 4 -2 2 1 現在8元,5小時 ↑ ans = min(ans,5) 錢超過所以下指針向右 ↓ 3 0 4 -2 2 1 現在8元,4小時 ↑ ans = min(ans,5) 錢超過所以下指針向右 ↓ 3 0 4 -2 2 1 現在5元,4小時 ↑ 上指針到底所以下指針向右 接下來我就不模擬了,就都是一樣的東西 最後答案是4 ``` --- ## a880 DFS? [題目](http://203.64.191.163/ShowProblem?problemid=a880) 這題我題目命名是DFS?,學過DFS應該會很直覺想到這題是用DFS解 這題也的確可以用DFS解 但這裡是初階班,肯定是有其他更簡單的方法 輸出的東西都是由0和1組成 這些0和1就和二進位一樣 每個0和1的組合換成10進位 例如 是3個格子的話 $000 \implies 0$ $001 \implies 1$ $010 \implies 2$ $100 \implies 4$ $101 \implies 5$ $110 \implies 6$ $111 \implies 7$ 我們可以發現這其實就是 輸出$0到2^3-1$的十進位換成二進位 如果是4個格子 就是輸出$0到2^4-1$的十進位換成二進位 那要怎麼把十進位換成二進位呢 我們可以用$\%\ 2$來看十進位轉二進位的最後一位是0還是1 如果$\%\ 2 == 1$就代表二進位的最右邊是1 如果$\%\ 2 == 0$就代表二進位的最右邊是0 判斷完一個後可以用/2或是>>1的方式去掉最右邊的數字 ex 01101 / 2 = 0110 10010 >> 1 = 1001 虛擬碼 ``` decimal_to_binary(m,n): r for i <- 0 to n: if m % 2 == 1: r += '1' else: r += '0' m >>= 1 return r ```