--- tags:APCS 分治法 區間最大值 遞迴 --- # 2021年9月APCS 3. 幸運數字 題目連結:https://zerojudge.tw/ShowProblem?problemid=g277 ###### tags: `APCS` `分治法` `區間最大值` `遞迴` --- 題目說明與想法: 題目要求在一個陣列中不斷的:**找尋最小值=>求出左右區間和=>判斷符合條件的區間並進行下一次遞迴**,最後求出目標 ## 須注意事項: - 題目時限只有 0.5s,需要優化時間。最基本的如使用 ios::sync_with_stdio(false) 等輸入優化方式。 - 找尋最小值,如果使用 C++ 內建的 min_element()、find()、distance()等方式,會 TLE,應使用排序過的 deque<pair<int(value),int(index)> 的方式來找最小值 - 左右區間和,優使用前綴和 (prefix-sum) 來解決 - 前綴和的陣列要開到 long long int ,否則會溢位 - 其他像是 index 之類的問題也要注意 code (AC): ```C++= #include <bits/stdc++.h> using namespace std; int lucky; vector<int> table; vector<long long int> pre_sum; // 記得要開 long long deque<pair<int,int>> n_idx; void find_min(int left,int right,int &indx){ while(true){ pair<int,int> ind=n_idx.front(); if (ind.second>=left && ind.second<=right){ indx=ind.second; n_idx.pop_front(); break; } else n_idx.pop_front(); } } void finds(int left,int right){ // left,right 階包含在區間中,換句話說,區間為 [left,right] if (right==left) { lucky=table[left]; return; } int ind; find_min(left,right,ind); //cout<<"min_num:"<<min_num<<endl; long long int left_sum,right_sum; if (ind==left) left_sum=0; else left_sum=pre_sum[ind]-pre_sum[left]; if (ind==right) right_sum=0; else right_sum =pre_sum[right+1]-pre_sum[ind+1]; //cout<<"left_sum"<<left_sum<<endl; //cout<<"right_sum"<<right_sum<<endl; if (left_sum>right_sum) finds(left,ind-1); else if (left_sum<=right_sum) finds(ind+1,right); } int main() { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); int n; cin>>n; for (int i=0;i<n;i++){ int k; cin>>k; table.push_back(k); n_idx.push_back({k,i}); } sort(n_idx.begin(),n_idx.end()); pre_sum.push_back(0); pre_sum.push_back(table[0]); for (int i=2;i<=table.size();i++){ pre_sum.push_back(pre_sum[i-1]+table[i-1]); //cout<<pre_sum[i]<<" "; } //cout<<endl; finds(0,table.size()-1); cout<<lucky<<endl; return 0; } ``` 通過截圖:![](https://i.imgur.com/BInZDgQ.png) NA(90%) Code: ```C++= #include <bits/stdc++.h> using namespace std; int lucky; vector<int> table; vector<long long int> pre_sum; void finds(int left,int right){ // left,right 階包含在區間中,換句話說,區間為 [left,right] if (right==left) { lucky=table[left]; return; } int min_num=*min_element(table.begin()+left,table.begin()+right+1); //cout<<"min_num:"<<min_num<<endl; auto it=find(table.begin(),table.end(),min_num); int ind=distance(table.begin(),it); long long int left_sum,right_sum; if (ind==left) left_sum=0; else left_sum=pre_sum[ind]-pre_sum[left]; if (ind==right) right_sum=0; else right_sum =pre_sum[right+1]-pre_sum[ind+1]; //cout<<"left_sum"<<left_sum<<endl; //cout<<"right_sum"<<right_sum<<endl; if (left_sum>right_sum) finds(left,ind-1); else if (left_sum<=right_sum) finds(ind+1,right); } int main() { ios::sync_with_stdio(false); cin.tie(NULL); int n; cin>>n; for (int i=0;i<n;i++){ int k; cin>>k; table.push_back(k); } pre_sum.push_back(0); pre_sum.push_back(table[0]); for (int i=2;i<=table.size();i++){ pre_sum.push_back(pre_sum[i-1]+table[i-1]); //cout<<pre_sum[i]<<" "; } //cout<<endl; finds(0,table.size()-1); cout<<lucky<<endl; return 0; } ``` 4/20 更新 在側資加強版的題目中,發現會 stack overflow,因此參考別人的文章重寫了一個 ```C++= #include<iostream> #include<vector> //vector #include<utility> //pair #include<algorithm> //sort() #define fastio ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0) #define num first #define index second using namespace std; int n; vector<int>people; vector<pair<int,int>>num_index; int find_min(int L,int R){ while(true){ pair<int,int> i=num_index.back(); if(i.index>=L && i.index<=R){ int ret=i.index; num_index.pop_back(); return ret; } else num_index.pop_back(); } } int main(){ fastio; cin>>n; int a; people.push_back(0); for(int i=0;i<n;i++){ cin>>a; people.push_back(a); num_index.push_back({a,i+1}); } sort(num_index.begin(),num_index.end(),greater<pair<int,int>>()); vector<long long>prefix_sum(n+1); prefix_sum[0]=0; for(int i=1;i<=n;i++){ prefix_sum[i]=prefix_sum[i-1]+people[i]; } int L=1,R=n; while(L<R){ int pivot=find_min(L,R); if(prefix_sum[pivot-1]-prefix_sum[L-1]>prefix_sum[R]-prefix_sum[pivot]){ R=pivot-1; }else L=pivot+1; } cout<<people[R]<<"\n"; return 0; } ```