# APCS實作題2021年9月第3題:幸運數字 > 日期:2024年7月12日 > 作者:王一哲 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=g277) ## 題目 ### 問題描述 在 $n$ 個人之中,每個人被分配到一個號碼,記錄為 $a_1, a_2, \dots, a_n$,這些號碼彼此都不相同,我們要找出當中最幸運的一個。已知在 $[L, R]$ 區間之中,最幸運號碼定義為: 1. 當 $L = R$ 時,幸運號碼為 $a_L$ 2. 當 $L < R$ 時,找出當中最小號碼的位置 $m$,並以最小號碼所在的位置將整個區間區分為左邊 $[L, m-1]$ 與右邊 $[m+1, R]$,計算兩個區間各自的總和,總和比較大的區間即為幸運號碼所在的區間;如果兩個區間的總和相等,則幸運號碼位在右邊的區間,若區間不包含任何數字,總和視為 $0$。重複以上步驟直到收斂至1. 時即可找到幸運號碼。 請找出這 $n$ 個人之中的幸運號碼。 <br /> ### 輸入說明 第一行輸入一個正整數 $n~(1 \leq n \leq 3 \times 10^5$,接下來有 $n$ 個數字 $a_1, a_2, \dots, a_n ~(1 \leq a_1 \leq 10^7)$。 配分 - 50%:$1 \leq a_i \leq n$,所有數字都介於 $1 \sim n$。 - 50%:無其他限制 <br /> ### 輸出格式 輸出這 $n$ 個數字中的幸運數字數值 <br /> ### 範例輸入1 ``` 5 4 2 3 1 5 ``` ### 範例輸出1 ``` 4 ``` ### 範例輸入2 ``` 8 3 9 4 5 1 6 2 8 ``` ### 範例輸出2 ``` 9 ``` <br /> ## Python 程式碼 ### 寫法1,遞迴 很直接的寫法,完全按照題目的條件寫程式碼,但會因為計算次數過多,從第18筆測資開始超時。 ```python= def solve(ns): # 自訂函式 d = len(ns) # 取 ns 的長度 d if d == 1: return ns[0] # 如果 d 等於 1,已找到答案,回傳 ns[0] idx = ns.index(min(ns)) # 找 ns 中最小值的索引值 idx if idx == 0: return solve(ns[1:]) # 如果 idx 等於 0,用 ns[1:] 代入 solve elif idx == d-1: return solve(ns[:-1]) # 如果 idx 等於 -1,用 ns[:-1] 代入 solve else: # 其它狀況 left = ns[:idx] # idx 左側串列 right = ns[idx+1:] # idx 右側串列 if sum(left) > sum(right): return solve(left) # 如果 left 和較大,用 left 代入 solve else: return solve(right) # 否則用 right 代入 solve n = int(input()) # 讀取 n arr = list(map(int, input().split())) # 讀取串列 arr print(solve(arr)) # arr 代入 solve 求解 ``` <br /><br /> ### 寫法2,修改陣列內容,還是會超時 很直接的寫法,完全按照題目的條件寫程式碼,但會因為計算次數過多,從第18筆測資開始超時。 ```python= n = int(input()) # 讀取 n arr = list(map(int, input().split())) # 讀取串列 arr while len(arr) > 1: # 串列長度大於1繼續執行 d = len(arr) # 串列長度 idx = arr.index(min(arr)) # 找最小值的索引值 idx if idx == 0: arr = arr[1:] # 如最小值在最左邊,由索引值 1 開始取新的串列 elif idx == d-1: arr = arr[:-1] # 如最小值在最右邊,由開頭取到倒數第2位 else: # 其它狀況 left = arr[:idx] # 左側串列 right = arr[idx+1:] # 右側串列 if sum(left) > sum(right): arr = left.copy() # 如果左側串列和較大,arr 改為 left 的內容 else: arr = right.copy() # 否則 arr 改為 right 的內容 print(arr[0]) # 最後只剩下一項就是答案 ``` <br /><br /> ### 寫法3,利用排序及前綴和 從吳邦一教授的解題影片〈[PyAp45 Python APCS 題解:幸運數字(AP202109_3), ZeroJudge g277](https://youtu.be/ADu5T02-PCY?si=6GAGISpukfxztC7z)〉中看到的寫法,費時最久約為 1.1 s,使用記憶體最多約為 90.1 MB,通過測試。 ```python= n = int(input()) # 讀取 n arr = [0] + list(map(int, input().split())) # 讀取串列 arr psum = arr.copy() # 前綴和 for i in range(1, n): psum[i] += psum[i-1] maxIdx = [(a, i) for i, a in enumerate(arr)] # 將 arr 的值與索引值組成數組再放進串列 maxIdx.sort(reverse=True) # 依照值由大到小排序 maxIdx.pop() # 移除最後一筆 (0, 0) left, right = 1, n # 左、右側索引值 while left < right: # 結束時 left == right while True: # 一直由 maxIdx 最後面取出的資料及索引值 a, i = maxIdx.pop() if i >= left and i <= right: break # 直到索引值在 [left, right] 之間為止 lsum = psum[i-1] - psum[left-1] # 左側加總 rsum = psum[right] - psum[i] # 右側加總 if lsum > rsum: right = i-1 # 幸運數字在左側,重設 right else: left = i+1 # 幸運數字在右側,重設 left print(arr[left]) # 印出答案 ``` <br /><br/> ## C++ 程式碼 由 Python 程式碼寫法3修改而成,費時最久約為 82 ms,使用記憶體最多約為 9.5 MB,通過測試。**加總的部分可能會超過 int 的上限造成溢位,改用 long long 才不會出事。** ```cpp= #include <iostream> #include <vector> #include <utility> #include <algorithm> typedef long long LL; using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); // 加速 int n; cin >> n; // 讀取 n vector<LL> arr (n+1, 0), psum (n+1, 0); // 資料 arr,前綴和 psum,為了避免出界,最前面加上 0 vector<pair<LL, LL>> maxIdx (n, pair<LL, LL> ()); // 排序用的陣列,資料為 (a, i) for(int i=1; i<=n; i++) { // 執行 n 次 cin >> arr[i]; // 讀取資料填入 arr[i] psum[i] += psum[i-1] + arr[i]; // 計算前綴和 maxIdx[i-1] = make_pair(arr[i], i); // (arr[i], i) 組成 pair 放入 maxIdx } sort(maxIdx.begin(), maxIdx.end(), greater<>()); // 依照 a 由大到小排序 int left = 1, right = n; // 左、右側索引值 while(left < right) { // 結束時 left == right int a, i; // 暫存資料及索引值的變數 while(true) { // 一直由 maxIdx 最後面取出的資料及索引值 a = maxIdx.back().first; i = maxIdx.back().second; maxIdx.pop_back(); if (i >= left && i <= right) break; // 直到索引值在 [left, right] 之間為止 } LL lsum = psum[i-1] - psum[left-1]; // 左側加總 LL rsum = psum[right] - psum[i]; // 右側加總 if (lsum > rsum) right = i-1; // 幸運數字在左側,重設 right else left = i+1; // 幸運數字在右側,重設 left } cout << arr[left] << "\n"; // 印出答案 return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`