Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

Partition Equal Subset Sum ใน C++

โจทย์: Partition Equal Subset Sum

ให้อาร์เรย์จำนวนเต็มบวก nums ไม่ว่างเปล่า ตรวจสอบว่าสามารถแบ่งออกเป็น 2 ส่วนย่อย (subsets) ที่มี ผลรวมเท่ากัน** ได้หรือไม่

ตัวอย่าง: nums = [1, 5, 11, 5]

ผลรวมรวม = 22, เป้าหมายแต่ละส่วน = 11

สามารถแบ่งได้: [1, 5, 5] และ [11] → true

อัลกอริทึม: 0/1 Knapsack (Subset Sum) DP

หากผลรวมรวมเป็นคี่ → ไม่สามารถแบ่งครึ่งได้ → false

เป้าหมาย: หา subset ที่ผลรวม = target = sum / 2

กำหนด dp[i] = true หากสามารถทำผลรวม i ได้จาก subset ของ nums

Initialization:

dp[0] = true (ผลรวม 0 ทำได้ตลอด: เลือกชุดว่าง)

Transition (0/1 Knapsack - วนย้อนกลับ):

สำหรับแต่ละ num ใน nums:

for (int i = target; i >= num; i--)
    dp[i] = dp[i] || dp[i - num];

สำคัญ: ต้องวน i จาก target ลงมา num (ย้อนกลับ) เพื่อไม่ให้ใช้ num เดียวกันหลายครั้ง (0/1 knapsack) หากวนจากน้อยไปมาก จะกลายเป็น unbounded knapsack (ใช้ซ้ำได้)

ตัวอย่างโค้ด C++

#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if (sum & 1) return false; // ผลรวมคี่
        
        int target = sum / 2;
        vector<bool> dp(target + 1, false);
        dp[0] = true;
        
        for (int num : nums) {
            for (int i = target; i >= num; i--) {
                dp[i] = dp[i] || dp[i - num];
            }
        }
        return dp[target];
    }
};

int main() {
    Solution ob;
    vector<int> v = {1, 5, 11, 5};
    cout << (ob.canPartition(v) ? "true" : "false") << endl;
    return 0;
}

Input

[1,5,11,5]

Output

true

ความซับซ้อน (Complexity)

  • เวลา: O(n × target) โดย target = sum/2
  • พื้นที่: O(target)

ข้อจำกัด: ใช้ได้ก็ต่อเมื่อ target ไม่ใหญ่เกินไป (sum ≤ ~20000) หาก sum ใหญ่มาก ต้องใช้ Meet-in-the-Middle หรือ Approximation

แก้ไขจากโค้ดต้นฉบับ: โค้ดต้นฉบับมี for(int j =sum;j-x>=0;j--) ซึ่ง j-x >= 0 เทียบเท่า j >= x เขียนให้ชัดเจนขึ้น และใช้ accumulate แทนลูปรวมเอง