LeetCode 494 目标和
给你一个整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 +
或 -
,然后串联起所有整数,可以构造一个表达式:
例如,nums = [2, 1]
,可以在 2
之前添加 +
,在 1
之前添加 -
,然后串联起来得到表达式 +2-1
。
返回可以通过上述方法构造的、运算结果等于 target
的不同表达式的数目。
动态规划
此处需要注意的是,要求对于每一个数字都需要添加一个 +
或 -
运算符。并且只包含 +
和 -
运算符,顺序并不会影响结果。此处依旧是按照0/1背包问题的方式进行问题的划分。
动态规划数组的定义:定一个数组
dp[2001]
其中dp[i]=j
表示,在0-0
元素中进行选择,能够得到值为i-1000
的表达式有j
个。动态规划数组的推导:在第
k
次迭代中,从0-k
的元素中生成表达式,相较于上一次,仅仅是多了元素nums[k]
,那么对于nums[k]
前的运算符只有-
或+
两种。那么dp[i]=dp[i-nums[k]]+dp[i+nums[k]]
这样的更新方式,本质上就是在穷举,通过一个双for循环完成穷举。动态规划数组的初始化:对于
dp[2001]
首先全部初始化为0,在处理第一个元素分别正负的问题dp[1000+nums[0]]+=1
dp[1000-nums[0]]+=1
。动态规划数组的遍历顺序:对于外层循环,需要不断将元素加入到表达式中,外层循环遍历
nums
中的元素。内层循环便利数组的中的元素。举例验证:
nums[2,1],target=1
初始时dp[1002]=1,dp[998]=1
,第一次迭代dp[1001]=dp[1000]+dp[1002]=1
验证成立。
1 | class Solution { |
在上述的实现过程中,本质上是在使用动态规划的方式将表达式求值的全部可能都穷举了出来,实现的方式和回溯法类似,时间复杂度
对问题进行转化:
对整个数组 nums
进行求和,最终的和值为
也就将问题转化为了,在数组 nums
中选取元素,是的元素的和等于 nums
中找到元素组合为表达式计算得到 target
的前提下,但若是不能找到任何方案,这样计算就会出现问题,因此需要增加一层判断,判断
动态规划数组的定义:定义数组
dp[neg+1]
其中dp[i]=j
表示,在0-0
个元素中选取总和为i
的方案有j
种。动态规划数组的推导:在第
k
次迭代中,从0-k
个元素中进行选取,此处相较于上一次,仅有nums[k]
这个元素,不同,对应两种方式:选取或不选。那么递推公式dp[i]=dp[i]+dp[i-nums[k]] if i-nums[k]>=0 else dp[i]
动态规划数组的初始化:初始时全部初始化为0,然后
dp[nums[0]]=1(if nums[0]<=neg),dp[0]=1
。数组的遍历顺序:在外层循环中将数组中的元素逐个添加,因此外层循从左向右遍历的数组,在内层循环中则是遍历动态规划数组,在计算
dp[i]
时需要使用dp[i-nums[k]]
的值,从右向左遍历。举例验证:
nums={2,1},target=1
那么neg=1
初始时dp={1,0}
第一次迭代:dp[1]=dp[1]+dp[0]=1
验证正确。
代码实现:
1 | class Solution { |
回溯法实现:
1 | int count=0; |