置頂

我的 VPython 教學文件 (HackMD 版本)

VPython 教學文件目錄 安裝及測試 基本語法 等速度直線運動 自由落下 終端速度 水平抛射 使用For迴圈計算水平抛射資料 斜向抛射 圓周運動 簡諧運動 單擺 木塊彈簧系統分離 重力及簡諧 行星運動 相疊木塊 雙重簡諧運動 一維彈性碰撞 ...

熱門文章

2026年10月20日 星期二

LeetCode 解題筆記:3886. Sum of Sortable Integers

作者:王一哲
日期:2026年10月20日


LeetCode 題目連結:3886. Sum of Sortable Integers

解題想法


困難題。題目給一個長度為 $n ~(1 \leq n \leq 10^5)$ 的整數陣列 $nums ~(1 \leq nums[i] \leq 10^5)$,如果將 $nums$ 切成長度為 $k$ 的子陣列,如果可以將子陣列以滾動的方式使得 $nums$ 由小到大排序,求所有符合條件的 $k$ 值加總。

這題下方有提示,基本上就是按照題示寫程式碼。寫一個檢驗 $k$ 是否符合條件的自訂函式 $check$,定義前一個子陣列的最小值 prev_max,預設為小於題目測資範圍的 $-100000000$。用一個 for 迴圈檢查分割 $nums$ 為長度為 $k$ 的子陣列,定義子陣列最小值 sub_min、最大值 sub_max,兩者皆預設為$nums[i]$,子陣列遞減次數 $drop$ 預設為 $0$。裡面再用一個 for 迴圈,檢查長度為 $k$ 的區間內部。如果子陣列可以用滾動的方式由小到大排列,則子陣列中遞減的次數最多只有 1 次;如果遞減次數為 1 次,則子陣列的首項必須大於、等於末項;這個子陣列的最小值必須大於、等於前一個子陣列的最大值。

由於題目要將 $nums$ 分割為長度均為 $k$ 的子陣列,因此 $k$ 一定是 $n$ 的因數,再加上 $n$ 的最大值為 $100000$,要測試的因數 $k$ 最大為 $\sqrt{n} \approx 316$,如果在這個範圍內找到因數 $k$,同時再檢查對應的因數 $p = n / k$,需要檢查的次數並不多,可以用 for 迴圈暴力解。

Python 程式碼


Runtime: 2965 ms, beats 29.67%. Memory: 31.36 MB, beats 69.23%.
class Solution:
    def sortableIntegers(self, nums: list[int]) -> int:
        n = len(nums)

        # 1. 自訂函式,檢驗 nums 切成長度 k 的子陣列是否能排序
        def check(k):
            prev_max = -float('inf')  # 前一個子陣列的最小值
            for i in range(0, n, k):  # 分割 nums 為長度 k 的子陣列
                sub_min = sub_max = nums[i]  # 子陣列最小值、最大值,預設為 nums[i]
                drop = 0  # 遞減次數
                # 檢查長度 k 的區間內部
                for j in range(i, i + k - 1):
                    if nums[j] > nums[j + 1]:  # 遞減
                        drop += 1
                    # 更新 sub_min, sub_max
                    if nums[j + 1] < sub_min:
                        sub_min = nums[j + 1] 
                    elif nums[j + 1] > sub_max:
                        sub_max = nums[j + 1]
                
                # 條件1,如果遞減次數大於1,無法排序
                if drop > 1: return False
                # 條件2,如果遞減1次,子陣列末項必須小於、等於首項
                if drop == 1 and nums[i + k - 1] > nums[i]:
                    return False
                # 條件3,這一區最小值大於、等於前一區最大值
                if sub_min < prev_max: return False

                # 更新 prev_max
                prev_max = sub_max
            
            # 通過測試,回傳 True
            return True
        
        # 2. 找出 n 的所有因數 k,呼叫 check 測試因數 k
        ans = 0  # 答案
        for k in range(1, int(n**0.5) + 1):
            if n % k == 0:
                # 驗證因數 k
                if check(k): ans += k
                # 驗證對稱的因數 n // k,且 k != n // k
                if k != n // k:
                    if check(n // k): ans += n // k
        return ans


C++ 程式碼


Runtime: 42 ms, beats 89.71%. Memory: 88.26 MB, beats 96.32%.
class Solution {
public:
    // 自訂函式 check,檢驗將 nums 切成長度 k 的子陣列是否可排序
    bool check(int k, const vector<int>& nums) {
        // 長度,前一個子陣列的最大值
        int n = (int)nums.size(), prev_max = -1000000000;
        // 檢查各個長度為 k 的子陣列
        for(int i = 0; i < n; i += k) {
            // 遞減次數,子陣列的最大值、最小值,預設為 nums[j]
            int drop = 0, sub_max = nums[i], sub_min = nums[i];
            for(int j = i; j < i + k - 1; j++) {
                // 遞減
                if (nums[j + 1] < nums[j]) {
                    drop++;
                }
                // 更新子陣列最大值、最小值
                if (nums[j + 1] > sub_max) {
                    sub_max = nums[j + 1];
                } else if (nums[j + 1] < sub_min) {
                    sub_min = nums[j + 1];
                }
            }
            // 條件1,遞減次數不能大於 1
            if (drop > 1) return false;
            // 條件2,遞減次數1次,子陣列首項必須大於、等於末項
            if (drop == 1 && nums[i] < nums[i + k - 1]) return false;
            // 條件3,子陣列最小值必須大於、等於前一個子陣列的最大值
            if (sub_min < prev_max) return false;
            // 更新 prev_max
            prev_max = sub_max;
        }
        return true;  // 通過測試,回傳 true
    }

    int sortableIntegers(vector<int>& nums) {
        // 測試 nums 長度 n 所有的因數
        int n = (int)nums.size(), ans = 0;
        for(int k = 1; k <= (int)sqrt(n); k++) {
            if (n % k == 0) {
                if (check(k, nums)) ans += k;
                int p = n / k;  // 對應的因數
                if (k != p) {  // 避免相等的值
                    if (check(p, nums)) ans += p;
                }
            }
        }
        return ans;
    }
};


C 語言程式碼


Runtime: 84 ms, beats 69.57%. Memory: 16.58 MB, beats 100.00%.
// 自訂函式 check,檢驗將 nums 切成長度 k 的子陣列是否可排序
int check(int k, int* nums, int numsSize) {
    // 前一個子陣列的最大值
    int prev_max = -1000000000;
    // 檢查各個長度為 k 的子陣列
    for(int i = 0; i < numsSize; i += k) {
        // 遞減次數,子陣列的最大值、最小值,預設為 nums[j]
        int drop = 0, sub_max = nums[i], sub_min = nums[i];
        for(int j = i; j < i + k - 1; j++) {
            // 遞減
            if (nums[j + 1] < nums[j]) {
                drop++;
            }
            // 更新子陣列最大值、最小值
            if (nums[j + 1] > sub_max) {
                sub_max = nums[j + 1];
            } else if (nums[j + 1] < sub_min) {
                sub_min = nums[j + 1];
            }
        }
        // 條件1,遞減次數不能大於 1
        if (drop > 1) return 0;
        // 條件2,遞減次數1次,子陣列首項必須大於、等於末項
        if (drop == 1 && nums[i] < nums[i + k - 1]) return 0;
        // 條件3,子陣列最小值必須大於、等於前一個子陣列的最大值
        if (sub_min < prev_max) return 0;
        // 更新 prev_max
        prev_max = sub_max;
    }
    return 1;  // 通過測試,回傳 1
}

int sortableIntegers(int* nums, int numsSize) {
    // 測試 nums 長度 n 所有的因數
    int ans = 0;
    for(int k = 1; k <= (int)sqrt(numsSize); k++) {
        if (numsSize % k == 0) {
            if (check(k, nums, numsSize)) ans += k;
            int p = numsSize / k;  // 對應的因數
            if (k != p) {  // 避免相等的值
                if (check(p, nums, numsSize)) ans += p;
            }
        }
    }
    return ans;
}


沒有留言:

張貼留言