leetcode 494 目标和

题目

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例

输入: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

解析

原问题等同于: 找到nums一个正子集P和一个负子集N,使得总和等于target。即sum( P) - sum(N) == target(就是减号,因为N是负数)

即 sum( P) + sum(N) + ( sum( P) - sum(N) ) == sum( P) + sum(N) + target;
即 左边等于2 * sum( P) , 右边等于 sum(nums) + target(因为sum( P) + sum(N) = sum(nums))

其中target + sum(nums)必须 >=0且为偶数,否则等式不可能成立(这个不理解也不太重要,先看后面的重点)
则问题转换为:存在多少个子集P,使sum( P) == (target + sum(nums))/2;

动态规划五部曲:
1.确定dp数组以及下标的含义
dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法,注意这里和之前的问题不同,之前的比如是能否填满背包,或者是别的,这里是有多少种方法;
2.确定递推公式
求组合类问题的公式,都是类似这种:dp[j] += dp[j - nums[i]] (背下来
3.dp数组如何初始化
本题需要初始化dp[0] = 1,表示填满容积为0的背包,有一种方法(不然的话后面全是0了)
4.确定遍历顺序
一维背包的话,外层遍历物品,内层遍历背包且倒序

func findTargetSumWays(nums []int, target int) int {
    sum := 0
    for _, v := range nums {
        sum += v
    }

    if abs(target) > sum {
        return 0
    }

    if (sum + target) % 2 == 1 {
        return 0
    }

    // 计算背包大小
    bag := (sum + target) / 2
    // 定义bp数组
    dp := make([]int, bag+1) //感觉这种题目,要是实在不知道有多大,可以先初始化一个较大的值
    // 初始化dp[j],含义是填满容量为j这么大体积的背包,有dp[j]种方法
    dp[0] = 1
    // 遍历顺序
    for i := 0; i < len(nums); i++ { // 遍历物品放在外循环,遍历背包再内循环且倒序
        for j := bag; j >= nums[i]; j-- {
            // 推导公式
            dp[j] += dp[j-nums[i]]
        }
    }
    return dp[bag]
}

func abs(x int) int {
    return int(math.Abs(float64(x)))
}

再次强调下,本题是填满容量为j的背包,有多少种方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值