列表

详情


416. 分割等和子集

给你一个 只包含正整数 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

 

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

 

提示:

相似题目

划分为k个相等的子集

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: bool canPartition(vector<int>& nums) { } };

rust 解法, 执行用时: 5 ms, 内存消耗: 2.3 MB, 提交时间: 2025-04-07 09:31:11

impl Solution {
    // dfs
    pub fn can_partition1(nums: Vec<i32>) -> bool {
        let s = nums.iter().sum::<i32>() as usize;
        if s % 2 != 0 {
            return false;
        }
        fn dfs(i: usize, j: usize, nums: &[i32], memo: &mut [Vec<i32>]) -> bool {
            if i == nums.len() {
                return j == 0;
            }
            if memo[i][j] != -1 { // 之前计算过
                return memo[i][j] == 1;
            }
            let x = nums[i] as usize;
            let res = j >= x && dfs(i + 1, j - x, nums, memo) || dfs(i + 1, j, nums, memo);
            memo[i][j] = if res { 1 } else { 0 }; // 记忆化
            res
        }
        let n = nums.len();
        let mut memo = vec![vec![-1; s / 2 + 1]; n]; // -1 表示没有计算过
        // 为方便起见,改成 i 从 0 开始
        dfs(0, s / 2, &nums, &mut memo)
    }

    // 递推
    pub fn can_partition2(nums: Vec<i32>) -> bool {
        let s = nums.iter().sum::<i32>();
        if s % 2 != 0 {
            return false;
        }
        let s = s as usize / 2; // 注意这里把 s 减半了
        let n = nums.len();
        let mut f = vec![vec![false; s + 1]; n + 1];
        f[0][0] = true;
        for (i, &x) in nums.iter().enumerate() {
            let x = x as usize;
            for j in 0..=s {
                f[i + 1][j] = j >= x && f[i][j - x] || f[i][j];
            }
        }
        f[n][s]
    }

    // 空间优化
    pub fn can_partition(nums: Vec<i32>) -> bool {
        let s = nums.iter().sum::<i32>();
        if s % 2 != 0 {
            return false;
        }
        let s = s as usize / 2; // 注意这里把 s 减半了
        let mut f = vec![false; s + 1];
        f[0] = true;
        let mut s2 = 0;
        for x in nums {
            let x = x as usize;
            s2 = (s2 + x).min(s);
            for j in (x..=s2).rev() {
                f[j] = f[j] || f[j - x];
            }
            if f[s] {
                return true;
            }
        }
        false
    }
}

javascript 解法, 执行用时: 3 ms, 内存消耗: 56.2 MB, 提交时间: 2025-04-07 09:29:57

/**
 * @param {number[]} nums
 * @return {boolean}
 */

// dfs
var canPartition1 = function(nums) {
    const s = _.sum(nums);
    if (s % 2) {
        return false;
    }
    const n = nums.length;
    const memo = Array.from({length: n}, () => Array(s / 2 + 1).fill(-1)); // -1 表示没有计算过
    function dfs(i, j) {
        if (i < 0) {
            return j === 0;
        }
        if (memo[i][j] !== -1) { // 之前计算过
            return memo[i][j] === 1;
        }
        const res = j >= nums[i] && dfs(i - 1, j - nums[i]) || dfs(i - 1, j);
        memo[i][j] = res ? 1 : 0; // 记忆化
        return res;
    }
    return dfs(n - 1, s / 2);
};

// 递推
var canPartition2 = function(nums) {
    let s = _.sum(nums);
    if (s % 2) {
        return false;
    }
    s /= 2; // 注意这里把 s 减半了
    const n = nums.length;
    const f = Array.from({length: n + 1}, () => Array(s + 1).fill(false));
    f[0][0] = true;
    for (let i = 0; i < n; i++) {
        const x = nums[i];
        for (let j = 0; j <= s; j++) {
            f[i + 1][j] = j >= x && f[i][j - x] || f[i][j];
        }
    }
    return f[n][s];
};

// 空间优化
var canPartition3 = function(nums) {
    let s = _.sum(nums);
    if (s % 2) {
        return false;
    }
    s /= 2; // 注意这里把 s 减半了
    const f = Array(s + 1).fill(false);
    f[0] = true;
    let s2 = 0;
    for (const x of nums) {
        s2 = Math.min(s2 + x, s);
        for (let j = s2; j >= x; j--) {
            f[j] = f[j] || f[j - x];
        }
        if (f[s]) {
            return true;
        }
    }
    return false;
};

// bitset
var canPartition = function(nums) {
    let s = _.sum(nums);
    if (s % 2) {
        return false;
    }
    s /= 2;
    let f = 1n;
    for (const x of nums) {
        f |= f << BigInt(x);
    }
    return (f >> BigInt(s) & 1n) === 1n;
};

python3 解法, 执行用时: 0 ms, 内存消耗: 17.6 MB, 提交时间: 2025-04-07 09:28:00

class Solution:
    # dfs
    def canPartition1(self, nums: List[int]) -> bool:
        @cache  # 缓存装饰器,避免重复计算 dfs 的结果(记忆化)
        def dfs(i: int, j: int) -> bool:
            if i < 0:
                return j == 0
            return j >= nums[i] and dfs(i - 1, j - nums[i]) or dfs(i - 1, j)

        s = sum(nums)
        return s % 2 == 0 and dfs(len(nums) - 1, s // 2)

    # 递推
    def canPartition2(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s % 2:
            return False
        s //= 2  # 注意这里把 s 减半了
        n = len(nums)
        f = [[False] * (s + 1) for _ in range(n + 1)]
        f[0][0] = True
        for i, x in enumerate(nums):
            for j in range(s + 1):
                f[i + 1][j] = j >= x and f[i][j - x] or f[i][j]
        return f[n][s]
        
    # 空间优化
    def canPartition3(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s % 2:
            return False
        s //= 2  # 注意这里把 s 减半了
        f = [True] + [False] * s
        s2 = 0
        for i, x in enumerate(nums):
            s2 = min(s2 + x, s)
            for j in range(s2, x - 1, -1):
                f[j] = f[j] or f[j - x]
            if f[s]:
                return True
        return False
        
    # bitset
    def canPartition(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s % 2:
            return False
        s //= 2
        f = 1
        for x in nums:
            f |= f << x
        return (f >> s & 1) == 1

java 解法, 执行用时: 7 ms, 内存消耗: 43.4 MB, 提交时间: 2025-04-07 09:26:17

import java.math.BigInteger;

class Solution {
    // dfs
    public boolean canPartition1(int[] nums) {
        int s = 0;
        for (int num : nums) {
            s += num;
        }
        if (s % 2 != 0) {
            return false;
        }
        int n = nums.length;
        int[][] memo = new int[n][s / 2 + 1];
        for (int[] row : memo) {
            Arrays.fill(row, -1); // -1 表示没有计算过
        }
        return dfs(n - 1, s / 2, nums, memo);
    }

    private boolean dfs(int i, int j, int[] nums, int[][] memo) {
        if (i < 0) {
            return j == 0;
        }
        if (memo[i][j] != -1) { // 之前计算过
            return memo[i][j] == 1;
        }
        boolean res = j >= nums[i] && dfs(i - 1, j - nums[i], nums, memo) || dfs(i - 1, j, nums, memo);
        memo[i][j] = res ? 1 : 0; // 记忆化
        return res;
    }

    // 递推
    public boolean canPartition2(int[] nums) {
        int s = 0;
        for (int num : nums) {
            s += num;
        }
        if (s % 2 != 0) {
            return false;
        }
        s /= 2; // 注意这里把 s 减半了
        int n = nums.length;
        boolean[][] f = new boolean[n + 1][s + 1];
        f[0][0] = true;
        for (int i = 0; i < n; i++) {
            int x = nums[i];
            for (int j = 0; j <= s; j++) {
                f[i + 1][j] = j >= x && f[i][j - x] || f[i][j];
            }
        }
        return f[n][s];
    }

    // 空间优化
    public boolean canPartition3(int[] nums) {
        int s = 0;
        for (int x : nums) {
            s += x;
        }
        if (s % 2 != 0) {
            return false;
        }
        s /= 2; // 注意这里把 s 减半了
        boolean[] f = new boolean[s + 1];
        f[0] = true;
        int s2 = 0;
        for (int x : nums) {
            s2 = Math.min(s2 + x, s);
            for (int j = s2; j >= x; j--) {
                f[j] = f[j] || f[j - x];
            }
            if (f[s]) {
                return true;
            }
        }
        return false;
    }

    // bitset
    public boolean canPartition(int[] nums) {
        int s = 0;
        for (int x : nums) {
            s += x;
        }
        if (s % 2 != 0) {
            return false;
        }
        s /= 2; // 注意这里把 s 减半了
        BigInteger f = BigInteger.ONE;
        for (int x : nums) {
            f = f.or(f.shiftLeft(x)); // f |= f << x;
        }
        return f.testBit(s); // 判断 f 中第 s 位是否为 1
    }
}

golang 解法, 执行用时: 0 ms, 内存消耗: 5.2 MB, 提交时间: 2025-04-07 09:24:10

// dfs
func canPartition1(nums []int) bool {
    s := 0
    for _, x := range nums {
        s += x
    }
    if s%2 != 0 {
        return false
    }
    n := len(nums)
    memo := make([][]int8, n)
    for i := range memo {
        memo[i] = make([]int8, s/2+1)
        for j := range memo[i] {
            memo[i][j] = -1 // -1 表示没有计算过
        }
    }
    var dfs func(int, int) bool
    dfs = func(i, j int) bool {
        if i < 0 {
            return j == 0
        }
        p := &memo[i][j]
        if *p != -1 { // 之前计算过
            return *p == 1
        }
        res := j >= nums[i] && dfs(i-1, j-nums[i]) || dfs(i-1, j)
        if res {
            *p = 1 // 记忆化
        } else {
            *p = 0
        }
        return res
    }
    return dfs(n-1, s/2)
}

// 递推
func canPartition2(nums []int) bool {
    s := 0
    for _, num := range nums {
        s += num
    }
    if s%2 != 0 {
        return false
    }
    s /= 2 // 注意这里把 s 减半了
    n := len(nums)
    f := make([][]bool, n+1)
    for i := range f {
        f[i] = make([]bool, s+1)
    }
    f[0][0] = true
    for i, x := range nums {
        for j := 0; j <= s; j++ {
            f[i+1][j] = j >= x && f[i][j-x] || f[i][j]
        }
    }
    return f[n][s]
}

// 空间优化
func canPartition3(nums []int) bool {
    s := 0
    for _, x := range nums {
        s += x
    }
    if s%2 != 0 {
        return false
    }
    s /= 2 // 注意这里把 s 减半了
    f := make([]bool, s+1)
    f[0] = true
    s2 := 0
    for _, x := range nums {
        s2 = min(s2+x, s)
        for j := s2; j >= x; j-- {
            f[j] = f[j] || f[j-x]
        }
        if f[s] {
            return true
        }
    }
    return false
}

// bitset
func canPartition(nums []int) bool {
    s := 0
    for _, x := range nums {
        s += x
    }
    if s%2 != 0 {
        return false
    }
    s /= 2
    f := big.NewInt(1)
    p := new(big.Int)
    for _, x := range nums {
        f.Or(f, p.Lsh(f, uint(x)))
    }
    return f.Bit(s) == 1
}

cpp 解法, 执行用时: 8 ms, 内存消耗: 14 MB, 提交时间: 2025-04-07 09:21:48

class Solution {
public:
    // 定义 dfs(i,j) 表示能否从 nums[0] 到 nums[i] 中选出一个和恰好等于 j 的子序列
    bool canPartition1(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 表示没有计算过
        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;
            }
            return res = j >= nums[i] && dfs(i - 1, j - nums[i]) || dfs(i - 1, j);
        };
        return dfs(n - 1, s / 2);
    }

    // 递推
    bool canPartition2(vector<int>& nums) {
        int s = reduce(nums.begin(), nums.end());
        if (s % 2) {
            return false;
        }
        s /= 2; // 注意这里把 s 减半了
        int n = nums.size();
        vector f(n + 1, vector<int>(s + 1));
        f[0][0] = true;
        for (int i = 0; i < n; i++) {
            int x = nums[i];
            for (int j = 0; j <= s; j++) {
                f[i + 1][j] = j >= x && f[i][j - x] || f[i][j];
            }
        }
        return f[n][s];
    }
    
    // 空间优化
    bool canPartition3(vector<int>& nums) {
        int s = reduce(nums.begin(), nums.end());
        if (s % 2) {
            return false;
        }
        s /= 2; // 注意这里把 s 减半了
        vector<int> f(s + 1);
        f[0] = true;
        int s2 = 0;
        for (int x : nums) {
            s2 = min(s2 + x, s);
            for (int j = s2; j >= x; j--) {
                f[j] |= f[j - x];
            }
            if (f[s]) {
                return true;
            }
        }
        return false;
    }
    
    // bitset做法
    bool canPartition(vector<int>& nums) {
        int s = reduce(nums.begin(), nums.end());
        if (s % 2) {
            return false;
        }
        s /= 2;
        bitset<10001> f; // sum(nums[i]) / 2 <= 10000
        f[0] = 1;
        for (int x : nums) {
            f |= f << x;
        }
        return f[s]; // 判断 f 中第 s 位是否为 1
    }
};

python3 解法, 执行用时: 1076 ms, 内存消耗: 13.4 MB, 提交时间: 2020-11-17 15:08:24

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        # 恰好装满的0-1背包问题
        s, n = sum(nums), len(nums)
        if s % 2 == 1:
            return False
        cap = s // 2
        dp = [False] * (cap + 1)
        dp[0] = True
        for i in range(1, n+1):
            for j in range(cap, nums[i-1]-1, -1):
                dp[j] = dp[j] or dp[j-nums[i-1]]
        return dp[cap]

上一题