题目:
给你一个整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式 :
- 例如,
nums = [2, 1]
,可以在2
之前添加'+'
,在1
之前添加'-'
,然后串联起来得到表达式"+2-1"
。
返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
算法:
方法一:dfs遍历搜索
func findTargetSumWays(nums []int, target int) int {
ans := 0
n := len(nums)
var dfs func(i, sum int)
dfs = func(i, sum int) {
if i == n {
// fmt.Println(i, sum)
if target == sum {
ans ++
}
return
}
dfs(i + 1, sum + nums[i])
dfs(i + 1, sum - nums[i])
}
dfs(0, 0)
return ans
}
方法二:动态规划
假设:nums的元素和sum,加上正负符号后,所有带-号元素之和neg,带+号元素之和pos。存在如下等式:
target = pos - neg = pos - neg + neg - neg = sum - 2neg
neg = (sum - target) / 2 所以sum - target必须为正偶数,才可能存在符合条件的组合。在存在解的情况下,问题转换为nums[i]存在多少子集,使得sum(nums[i]) == (sum - target) / 2。
dp[i][j]表示从nums[i]开始,元素和为j的组合个数
dp[i][j] = dp[i - 1][j] 不选nums[i]
dp[i - 1][j - nums[i]] + 1 选nums[i]
边界条件:
dp[0][j] = 1 j = 0
0 j > 0
func findTargetSumWays(nums []int, target int) int {
sum := 0
for i := range nums {
sum = sum + nums[i]
}
neg := (sum - target) / 2
if neg < 0 || (sum - target) % 2 != 0 {
return 0
}
n := len(nums)
dp := make([][]int, n + 1)
for i := range dp {
dp[i] = make([]int, neg + 1)
}
dp[0][0] = 1
for i := 1; i <= n; i ++ {
for j := 0; j <= neg; j ++ {
dp[i][j] = dp[i - 1][j]
if j >= nums[i - 1] {
dp[i][j] = dp[i][j] + dp[i - 1][j - nums[i - 1]]
}
}
}
return dp[n][neg]
}
方法二:动态规划 空间压缩
func findTargetSumWays(nums []int, target int) int {
sum := 0
for i := range nums {
sum = sum + nums[i]
}
neg := (sum - target) / 2
if neg < 0 || (sum - target) % 2 != 0 {
return 0
}
n := len(nums)
dp := make([]int, neg + 1)
dp[0] = 1
for i := 0; i < n; i ++ {
for j := neg; j >= nums[i]; j -- {
dp[j] = dp[j] + dp[j - nums[i]]
}
}
return dp[neg]
}