---
# System prepended metadata

title: 2021年9月APCS 3. 幸運數字
tags: [遞迴, 分治法, APCS, 區間最大值]

---

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