分割等和子集

GitHub跳转原题关系图返回列表

0/1 背包判断能否凑满 sum/2,倒序更新避免重复使用元素。

OJ: leetcodecn

题目 ID: partition-equal-subset-sum

难度:普及+/提高

标签:动态规划0/1背包

日期: 2026-07-29 12:47

题意

判断数组能否分成两个和相等的子集。

思路

若总和为奇数,不可能。否则目标为 sum/2,转化为 0/1 背包:从 nums 中选若干数,和恰好为 targetdp[i] 表示和 i 是否可达,倒序更新避免重复使用同一元素。

代码

cpp
#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 x : nums)
            for (int i = target; i >= x; i--)
                dp[i] = dp[i] || dp[i - x];
        return dp[target];
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> a(n);
    for (int &x : a)
        cin >> x;
    cout << Solution().canPartition(a) << '\n';
    return 0;
}
python
#!/usr/bin/env python3
from typing import List


class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s & 1:
            return False
        target = s // 2
        dp = [False] * (target + 1)
        dp[0] = True
        for x in nums:
            for i in range(target, x - 1, -1):
                dp[i] = dp[i] or dp[i - x]
        return dp[target]


def main():
    n = int(input())
    a = list(map(int, input().split()))
    print(Solution().canPartition(a))


if __name__ == "__main__":
    main()

复杂度

  • 时间复杂度:O(ntarget)O(n \cdot \text{target})
  • 空间复杂度:O(target)O(\text{target})

总结

分割等和子集是 0/1 背包的判定版本。倒序更新是关键:正序更新会导致同一元素被多次选取。