【力扣-背包】3、目标和(494)

399 阅读2分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

494. 目标和

题目描述

给你一个整数数组 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

解析

/**
 * @brief 动态规划
 * 转换为01背包问题
 * 例如当我们的 target 不为 −sum 和 sum 时,−sum 和 sum 就是两个对「目标状态」不可达的“额外”状态值,到达 −sum 或 sum 已经使用所有数值,对 target 不可达。
 * 我们可以从哪些数值使用哪种符号来分析,即划分为【负值部分】和【非负值部分】。
 * 假设非负值为 x, 负值总和为  -(sum - x )
 *      所以我们需要求得是:  x + (-(sum-x)) = x - (sum-x) = target
 *                      x = (target+sum) / 2
 *      此时问题转为:装满容量为x的背包,有几种方法。
 * 
 * 动态规划四部曲:
 *  1、确定dp数组以及下标含义
 *      dp[j] : 填满容量为j的背包,有dp[j]种方法
 *  
 *  2、确定递推公式
 *      dp[j]的来源:
 *          
 *          填满容量为 j -nums[i]的背包, 有 dp[j-nums[i]]种方法
 * 
 *          举例:
 *              nums[i] = 2 
 *              dp[3] , 填满背包容量为3的话,有 dp[3] 种方法
 *              那么只需要搞到一个2 (nums[i] , 有dp[3]中方法可以凑齐容量为3的背包)
 *
 */

代码

class SolutionDP
{
public:
    int findTargetSumWays(vector<int> &nums, int target)
    {
        // 求和
        int sum = 0;
        for (int num:nums)
        {
            sum += num;
        }    
        // 没有解决方案
        if (abs(target) > sum ||(target + sum) % 2 == 1 )
            return 0;
        int bagSize = (target + sum) / 2;
        vector<int> dp(bagSize + 1, 0);
        dp[0] = 1;
        // 先遍历物品
        for (int i = 0; i < nums.size(); i++)
        {
            // 再遍历背包
            for (int j = bagSize; j >= nums[i]; j--)
            {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
};