LeetCode 416

https://leetcode.cn/problems/partition-equal-subset-sum/description/

难度:中等

高频面试题汇总:https://www.yuweihung.com/posts/2025/lc-hot/

本题相当于能否从 nums 中选出一个子序列,其元素和恰好等于 s/2。这是 0-1 背包的恰好装满型问题。

dfs(i, j)=dfs(i−1, j−nums[i]) || dfs(i−1, j) ​ 时间复杂度:O(ns),其中 n 是 nums 的长度,s 是 nums 的元素和(的一半)。由于每个状态只会计算一次,动态规划的时间复杂度 = 状态个数 × 单个状态的计算时间。本题状态个数等于 O(ns),单个状态的计算时间为 O(1),所以动态规划的时间复杂度为 O(ns)。

空间复杂度:O(ns)。保存多少状态,就需要多少空间。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int s = reduce(nums.begin(), nums.end());
        if (s % 2) {
            return false;
        }

        int n = nums.size();
        vector memo(n, vector<int>(s / 2 + 1, -1)); // -1 表示没有计算过

        // lambda 递归函数
        auto dfs = [&](this auto&& dfs, int i, int j) -> bool {
            if (i < 0) {
                return j == 0;
            }
            int& res = memo[i][j]; // 注意这里是引用
            if (res != -1) { // 之前计算过
                return res;
            }
            if (j < nums[i]) {
                return res = dfs(i - 1, j); // 只能不选
            }
            return res = dfs(i - 1, j - nums[i]) || dfs(i - 1, j); // 选或不选
        };

        return dfs(n - 1, s / 2);
    }
};