分割等和子集-动态规划416-python&c++

python
没看答案,暴力回溯法。

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if sum(nums) % 2 == 1:
            return False

        nums = sorted(nums)
        target = sum(nums) // 2
        sumTrack = 0
        flag = False

        def backtrack(start):
            nonlocal sumTrack, target, flag

            if flag: return
            if sumTrack == target:
                flag = True
                return 
            
            for cho in range(start, len(nums)):
                sumTrack += nums[cho]
                if sumTrack <= target:
                    start = cho + 1
                    backtrack(start)
                sumTrack -= nums[cho]

        backtrack(0)
        return flag

动态规划,可以转化为01背包问题。

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        '''
        根据题意可知如果能够分割,则nums的和必为偶数,且target为一个子集的和,故可转化为01背包问题:
        背包容量:target
        可选择的物品:数组nums的元素
        物品重量:数组nums的元素值
        套01背包问题的模板即可,答案是背包能否装满
        '''
        if sum(nums) % 2 == 1:
            return False
        target = sum(nums) // 2

        dp = [[0] * (target+1) for _ in range(len(nums))]
        for j in range(target+1):
            if j >= nums[0]:
                dp[0][j] = nums[0]

        for i in range(len(nums)):
            for j in range(1, target+1):
                if j < nums[i]:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i])

        return dp[-1][-1] == target

01背包问题的二维dp数组,一般都可以替换成一维滚动数组,同样要注意背包遍历要倒序以防止重复添加物品

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        '''
        01背包问题可优化二维dp数组为一维滚动dp数组
        注意dp数组的遍历是从后往前的,因为可以防止物品被添加超过一次  
        '''
        if sum(nums) % 2 == 1:
            return False
        nums = sorted(nums)
        target = sum(nums) // 2

        dp = [0] * (target+1)
        for j in range(target+1):
            if j >= nums[0]:
                dp[j] = nums[0]

        for i in range(1, len(nums)):
            for j in range(target, -1, -1):
                if j < nums[i]:
                    dp[j] = dp[j]
                else:
                    dp[j] = max(dp[j], dp[j-nums[i]]+nums[i])

        return dp[-1] == target  

c++

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        // 思路: 转化成0-1背包问题,只需要求出sum(nums)/2,再看能否装满即可
        // state: dp[i]表示选择或不选择nums[i]能否凑齐背包
        // basecase: dp[0]=false
        // transfer: dp[j] = dp[j] || dp[j-num]; 前者为不选择nums[i], 后者为选择nums[i]
        // result: dp[sum/2]
        int sum = 0;
        for (auto& num : nums) {
            sum += num;
        }
        if (sum % 2 == 1) return false;
        int amount = sum / 2;
        vector<bool> dp(amount+1, false);
        dp[0] = true;

        for (auto& num : nums) {
            // 这里必须倒序遍历,因为每个元素只能被选择一次(0-1背包)
            // 当每个元素都可以被选择多次时,也可以正序遍历(完全背包)
            for (int j = amount; j >= 1; j--) {
                if (j - num >= 0) {
                    dp[j] = dp[j] || dp[j-num];
                }
            }
        }

        return dp[amount];
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值