LeetCode 416 分割等和子集 Leetcode 494 目标和(01背包的应用)

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

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

为什么可以转化为01背包?
物品选择方式只有两种要么选择,要么不选择,这些数字可以看做是背包中的物品。与01背包不同的是,01背包的容量是不超过某个数,而本题目是等于某个数。
f[j] 表示和为j 的 nums 是否存在。既然是两个等和子集,那么该和一定为偶数,如果sum等于奇数,那么不存在。最后返回的是f[sum/2]。

f[j]=f[j] || f[j-nums[i]]; 的理解:

  • 不选择 nums[i],如果在 [0, i - 1] 这个子区间内已经有一部分元素,使得它们的和为 j ,那么 dp[i][j] = true;
  • 选择 nums[i],如果在 [0, i - 1] 这个子区间内就得找到一部分元素,使得它们的和为 j - nums[i]

参考链接 比较全面的总结01背包与完全背包的题目链接

背包问题解法:

  • 01 背包问题:
    如果是 01 背包,即数组中的元素不可重复使用,外循环遍历 arrs,内循环遍历 target,且内循环倒序:
  • 完全背包问题:
    (1)如果是完全背包,即数组中的元素可重复使用并且不考虑元素之间顺序,arrs 放在外循环(保证 arrs 按顺序),target在内循环。且内循环正序
    (2)如果组合问题需考虑元素之间的顺序,需将 target 放在外循环,将 arrs 放在内循环,且内循环正序。

链接:https://leetcode-cn.com/problems/word-break/solution/yi-tao-kuang-jia-jie-jue-bei-bao-wen-ti-kchg9/

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        int sum=0;
        for(auto x:nums) sum+=x;
        if(sum%2==1) return false;
        int target = sum/2;
        vector<int> f(target+1);
        f[0]=true;//初始化条件可以是false,不过状态转移方程会发生变化
        for(int i=0;i<n;i++)
        {
            for(int j=target;j>=nums[i];j--)
            {
                f[j]=f[j] || f[j-nums[i]]; 
            }
        }
        return f[target];
    }
};

目标和:
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1

这些数字可以看做是01背包中的物品,与上题目不同的是,该题目中的数字可正可负。假设满足条件的组合中正数之和 x 负数之和(变成负数的数字的和,不是转换为负数之后再做和) y,x-y = target,x+y = sum,那么 x =(s+sum)/2 意味着我们只要从数组中选出几个数这几个数的和是 x 即可(代码中记为了s)。又回到了01背包的常用方式,只要找到了组合中正数之和,那么其余的数字都是负数。最后返回的是f[s]
边界条件:target>sum || (target+sum)%2==1

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int n = nums.size();
        int sum=0;
        for(auto x:nums) sum+=x;
        int s =(target+sum)/2;
        if(target>sum || (target+sum)%2==1) return false;
        vector<int> f(s+1);
        f[0]=1;
        for(int i=0;i<n;i++)
        {
            for(int j=s;j>=nums[i];j--)
            {
                f[j]=f[j]+f[j-nums[i]];
            }
        }
        return f[s];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值