# APCS實作題2023年10月第1題:機械鼠 > 日期:2024年1月29日 > 作者:王一哲 > 題目來源:2023年10月實作題第1題 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=m370) ## 題目 ### 問題描述 有 $n$ 個位置上有食物,另外有一隻老鼠一開始位於位置 $x$。 老鼠在開始覓食前要選擇今天要往左邊或往右移動去尋找食物,經過食物時可以停下來吃食物,吃完後可以選擇繼續往相同方向移動,或者是結束今天的覓食。 請問老鼠最多能吃到多少個食物,以及最後停下來吃食物的位置。 <br /><br /> ### 輸入說明 第一行包含兩個整數:$x$ 和 $n$,以空格分隔。$x$ 代表老鼠的初始位置,$n$ 代表食物的數量。 第二行包含 $n$ 個整數,以空格分隔,表示每個食物的位置,且不會與老鼠位置重疊。 所有測試資料皆保證 $3 \leq n \leq 20$ 且 $n$ 是奇數,老鼠與食物位置範圍均為 $-100$ 到 $100$。 子題分數: - 60%:滿足 $n = 3$。 - 40%:一般情況。 <br /> ### 輸出說明 請輸出兩個整數,分別代表最多能吃到的食物數目和最後一個吃的食物停下的位置。 <br /> ### 範例輸入1 ``` 10 3 1 5 13 ``` ### 範例輸出1 ``` 2 1 ``` <br /> ### 範例輸入2 ``` 10 9 -1 13 12 16 100 -9 7 8 25 ``` ### 範例輸出2 ``` 5 100 ``` <br /> ## Python 程式碼 ### 方法1,依序讀取資料 費時最久約為 27 ms,使用記憶體最多約為 3.3 MB,通過測試。 ```python= x, n = map(int, input().split()) # 讀取初位置 x、食物數量 n pos = list(map(int, input().split())) # 讀取食物位置 left, right = x, x # 向左、右移動的終點 lsum, rsum = 0, 0 # 向左、右移動吃到的食物數量 for p in pos: # 依序由 pos 讀取食物位置 p if p > x: # 如果 p 大於 x rsum += 1 # 向右移動可以吃到的食物數量加 1 right = max(right, p) # 更新 right #if p > right: right = p # 如果 p > right,更新 right else: # 如果 p 小於 x lsum += 1 # 向左移動可以吃到的食物數量加 1 left = min(left, p) # 更新 left #if p < left: left = p # 如果 p < left,更新 left if rsum > lsum: # 如果 rsum > lsum,印出 rsum, right print("{:d} {:d}".format(rsum, right)) else: # 如果 rsum < lsum,印出 lsum, left print("{:d} {:d}".format(lsum, left)) ``` <br /><br /> ### 方法2,使用 sort 及 bisect_left 第6行是使用 bisect 函式庫中的 bisect_left,目的是要找出在已排序的 pos 之中,x 插入在何處還能維持由小到大排序,以範例輸入2為例,排序後的 pos 為 ```python [-9, -1, 7, 8, 12, 13, 16, 25, 100] ``` 若 x 為 10,則 bisect_left 回傳值 idx 為 4,因此向左可以吃到 4 個食物,向右可以吃到 5 個食物。 費時最久約為 18 ms,使用記憶體最多約為 3.3 MB,通過測試。 ```python= from bisect import bisect_left x, n = map(int, input().split()) # 讀取初位置 x、食物數量 n pos = list(map(int, input().split())) # 讀取食物位置 pos.sort() # 由小到大排序 idx = bisect_left(pos, x) # 使用二分搜尋法找 x 於 pos 插入且仍維持排序的位置 left, right = pos[0], pos[-1] # 向左、右移動的終點 lsum, rsum = idx, n-idx # 向左、右移動吃到的食物數量 print("{:d} {:d}".format(rsum, right) if rsum > lsum else "{:d} {:d}".format(lsum, left)) # 印出答案 ``` <br /><br /> ## C++ 程式碼 ### 方法1,依序讀取資料 費時最久約為 2 ms,使用記憶體最多約為 328 kB,通過測試。 ```cpp= #include <iostream> using namespace std; int main() { int x, n; cin >> x >> n; // 讀取初位置 x、食物數量 n int pos[n]; // 食物位置 for(int i=0; i<n; i++) { int p; cin >> p; pos[i] = p; } int left = x, right = x; // 向左、右移動的終點 int lsum = 0, rsum = 0; // 向左、右移動吃到的食物數量 for(int i=0; i<n; i++) { int p = pos[i]; // 依序由 pos 讀取食物位置 p if (p > x) { // 如果 p 大於 x rsum++; // 向右移動可以吃到的食物數量加 1 if (p > right) right = p; // 如果 p > right,更新 right } else { // 如果 p 小於 x lsum++; // 向左移動可以吃到的食物數量加 1 if (p < left) left = p; // 如果 p < left,更新 left } } if (rsum > lsum) { // 如果 rsum > lsum,印出 rsum, right cout << rsum << " " << right << "\n"; } else { // 如果 rsum < lsum,印出 lsum, left cout << lsum << " " << left << "\n"; } return 0; } ``` <br /><br /> ### 方法2,使用 sort 及 lower_bound 第14行是使用 algorithm 函式庫中的 lower_bound,目的是要找出在已排序的 pos 之中,x 插入在何處還能維持由小到大排序,因為回傳值為迭代器位址,如果要換算成索引值,需要再減去 vector 開頭的迭代器位址。以範例輸入2為例,排序後的 pos 為 ```cpp {-9, -1, 7, 8, 12, 13, 16, 25, 100} ``` 若 x 為 10,則 idx 為 4,因此向左可以吃到 4 個食物,向右可以吃到 5 個食物。 費時最久約為 3 ms,使用記憶體最多約為 332 kB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int x, n; cin >> x >> n; // 讀取初位置 x、食物數量 n vector<int> pos (n, 0); // 食物位置 for(int i=0; i<n; i++) { int p; cin >> p; pos[i] = p; } sort(pos.begin(), pos.end()); // 由小到大排序 int idx = lower_bound(pos.begin(), pos.end(), x) - pos.begin(); // 使用二分搜尋法找 x 於 pos 插入且仍維持排序的位置 int left = pos[0], right = pos[n-1]; // 向左、右移動的終點 int lsum = idx, rsum = n-idx; // 向左、右移動吃到的食物數量 if (rsum > lsum) { // 如果 rsum > lsum,印出 rsum, right cout << rsum << " " << right << "\n"; } else { // 如果 rsum < lsum,印出 lsum, left cout << lsum << " " << left << "\n"; } return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`