2601. Prime Subtraction Operation

Leetcode 連結

解法一

首先我們需要找出小於 1000 的所有質數,我們可以使用 Sieve of Eratosthenes 來快速找出這些質數。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
vector<int> Sieve(){
    vector<bool> p(1001, true);
    p[0] = p[1] = false;
    for (int i = 2; i * i <= 1000; i++){
        if (p[i]){
            for (int j = i * i; j <= 1000; j += i){
                p[j] = false;
            }
        }
    }
    vector<int> primes;
    for (int i = 0; i <= 1000; i++){
        if (p[i]){
            primes.push_back(i);
        }
    }
    return primes; // primes 是小於1000的所有質數所成的陣列
}

接下來我們想要讓 nums 變成嚴格遞增的陣列。

若是我們從前面處理到後面,想要讓陣列變成嚴格遞增的,在處理 nums[i] 時,nums[i] 能變得越小越好,同時不能小於等於 nums[i - 1]
也就是說我們想要找到最大的質數使得 nums[i] - prime > nums[i - 1],移項後發現這個質數要滿足 prime < nums[i] - nums[i - 1],我們可以使用 binary search 快速找到這樣的質數。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
bool primeSubOperation(vector<int>& nums) {
    int n = nums.size();
    for (int i = 0; i < n; i++){
        // 利用 binary search 找到第一個大於等於 nums[i] - nums[i - 1] 的質數
        // 他的前一個就會是小於 nums[i] - nums[i - 1] 的質數
        auto it = lower_bound(primes.begin(), primes.end(), i == 0? nums[i]: nums[i] - nums[i - 1]);
        if (it != primes.begin()){
            nums[i] -= *prev(it);
        }
        // 做完操作後若是發現不滿足 nums[i - 1] < nums[i],回傳 false
        if (i > 0 && nums[i] <= nums[i - 1]){
            return false;
        }
    }
    return true;
}

解法二

若是我們從後面處理到前面,那就要反過來處理。在處理 nums[i] 時,nums[i] 在扣除質數後能越大越好,同時不能大於等於 nums[i + 1]
也就是說我們想要找到最小的質數使得 nums[i] - prime < nums[i + 1],移項後發現這個質數要滿足 prime > nums[i] - nums[i + 1]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
bool primeSubOperation(vector<int>& nums) {
    int n = nums.size();
    // 注意現在是後面處理到前面
    for (int i = n - 2; i >= 0; i--){
        // nums[i] < nums[i + 1],什麼都不用做
        if (nums[i] < nums[i + 1]){
            continue;
        }
        // 利用 binary search 找到第一個大於 nums[i] - nums[i + 1] 的質數
        auto it = upper_bound(primes.begin(), primes.end(), nums[i] - nums[i + 1]);
        // 找到的prime要小於原本的數
        if (it != primes.end() && *it < nums[i]){
            nums[i] -= *it;
        }
        // 做完操作後若是發現不滿足 nums[i] < nums[i + 1],回傳 false
        if (nums[i] >= nums[i + 1]){
            return false;
        }
    }
    return true;
}

這題有很多特殊的情況要考慮: it 會不會是 prime.begin() 或是 prime.end(),找到的質數會不會大於原本的數。

當初在打週賽的時候處理很久,最後直接遍歷所有的質數(不使用 binary search)發現也會過,不過還是把好的解法寫一遍,多練習一下。