题目:
给你一个整数数组和一个目标值,对数组中的所有数字可以使用+或-使其和为目标值,问有多少种方式。
这道题之前用了回溯的方式来做,结果运行了1秒多,效率十分低下。这次借鉴了别人01背包的做法,感觉十分巧妙。
解法:
对于数组中的数字,可以将其分为两部分,一部分为使用+的数字,和为sumA,另一部分为使用了-的数字,和为sumB,根据题意可以得到如下的式子:
(1)sumA + sumB = sum
(2)sumA - sumB = target
将上述两个式子相加得到:
2sumA = sum + target,也就是sumA = (sum + target) / 2
那么本题就可以转为:
有一些物品,第i个物品的重量为nums[i],现有一个背包的容量为sumA,问有多少种方式可以将背包恰好填满。(01背包的本质就是第i个物品取或者不取)
需要注意的是nums数组都是非负整数,所以sumA、sumB、sum必须是非负整数,并且易知2sumA是偶数,所以sum + target必须是偶数,否则没有任何方式可以凑成目标和。
1. 确定dp数组
dp[i][j]表示前i个物品有多少种方式可以恰好装入容量为j的背包。
2. 转态转移方程
当背包容量大于等于第i个物品的重量,那么可以装入该物品,此时背包的结果为
dp[i][j] = dp[i-1][j-nums[i]] + dp[i-1][j]
否则当前背包的结果只能来前i-1个物品
dp[i][j] = dp[i-1][j]
3. 初始化dp数组
dp[i][0] 表示背包容量为0的情况下所有数字都不选,因此初始化为1
4. 确定遍历顺序
先遍历数字再遍历容量。数组从第一个数字开始,背包的容量可以为0,故需要从0开始遍历。
func findTargetSumWays(nums []int, target int) int {
n := len(nums)
dp := make([][]int, n+1)
sum :=0
for i:=0; i< n;i++{
sum += nums[i]
}
bagSize := (sum + target) / 2
//当target小于0时容量可能为0
if bagSize < 0{
bagSize = -bagSize
}
if (sum + target) %2 == 1{
return 0
}
for i:=0; i<=n;i++{
dp[i] = make([]int,bagSize+1)
dp[i][0] = 1
}
for i:=1; i<= n;i++{
for j:=0; j<=bagSize;j++{
//这里与最外层从1开始遍历对应
if j>=nums[i-1] {
dp[i][j] = dp[i-1][j-nums[i-1]] + dp[i-1][j]
}else{
dp[i][j] = dp[i-1][j]
}
}
}
return dp[n][bagSize]
}
这篇博客探讨了一种优化算法,将回溯法应用于求解整数数组中数字的加减组合以达到目标和的问题。作者通过转换问题为01背包问题,显著提高了算法效率。在新的解法中,将数组分为正负两部分,然后利用01背包的思想找到填充背包的方案数。文章强调了目标和与数组之和必须为偶数的条件,并给出了动态规划的解决方案,包括状态转移方程和初始化步骤。
972

被折叠的 条评论
为什么被折叠?



