给你一个整数数组 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提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
转:https://leetcode.cn/problems/target-sum/solution/mu-biao-he-by-leetcode-solution-o0cp/
动态规划
记数组的元素和为 sumsumsum,添加 −-− 号的元素之和为 negnegneg,则其余添加 +++ 的元素之和为 sum−negsum−negsum−neg,得到的表达式的结果为
(sum−neg)−neg=sum−2∗neg=target(sum-neg) - neg = sum - 2*neg=target(sum−neg)−neg=sum−2∗neg=target
即
neg=sum−target2neg = \frac{sum-target}{2}neg=2sum−target
由于数组 numsnumsnums 中的元素都是非负整数,negnegneg 也必须是非负整数,所以上式成立的前提是 sum−targetsum−targetsum−target 是非负偶数。若不符合该条件可直接返回 0。
若上式成立,问题转化成在数组 numsnumsnums 中选取若干元素,使得这些元素之和等于 negnegneg,计算选取元素的方案数。我们可以使用动态规划的方法求解。
定义二维数组 dpdpdp,其中 dp[i][j]dp[i][j]dp[i][j] 表示在数组 numsnumsnums 的前 iii 个数中选取元素,使得这些元素之和等于 jjj 的方案数。假设数组 numsnumsnums 的长度为 nnn,则最终答案为 dp[n][neg]dp[n][neg]dp[n][neg]。
当没有任何元素可以选取时,元素和只能是 0,对应的方案数是 1,因此动态规划的边界条件是:
dp[0][j]={1,j=00,j>=1dp[0][j]=\begin{cases} 1, j=0\\ 0, j >=1\end{cases}dp[0][j]={1,j=00,j>=1
当 1≤i≤n1≤i≤n1≤i≤n 时,对于数组 numsnumsnums 中的第 i 个元素 numnumnum(i 的计数从 1 开始),遍历 0≤j≤neg0≤j≤neg0≤j≤neg,计算 dp[i][j]dp[i][j]dp[i][j] 的值:
如果 j<numj<numj<num,则不能选 numnumnum,此时有 dp[i][j]=dp[i−1][j]dp[i][j]=dp[i-1][j]dp[i][j]=dp[i−1][j];
如果 j≥numj≥numj≥num,则如果不选 numnumnum,方案数是 dp[i−1][j]dp[i-1][j]dp[i−1][j],如果选 numnumnum,方案数是 dp[i−1][j−num]dp[i-1][j−num]dp[i−1][j−num],此时有 dp[i][j]=dp[i−1][j]+dp[i−1][j−num]dp[i][j]=dp[i-1][j]+dp[i-1][j−num]dp[i][j]=dp[i−1][j]+dp[i−1][j−num]
因此状态转移方程如下:
dp[i][j]={dp[i−1][j],j<numsdp[i−1][j]+dp[i−1][j−num],j>=numsdp[i][j]=\begin{cases} dp[i-1][j], j<nums \\ dp[i-1][j]+dp[i-1][j−num], j >=nums\end{cases}dp[i][j]={dp[i−1][j],j<numsdp[i−1][j]+dp[i−1][j−num],j>=nums
最终得到 dp[n][neg]dp[n][neg]dp[n][neg] 的值即为答案。
由此可以得到空间复杂度为 O(n×neg)O(n×neg)O(n×neg) 的实现.
class Solution:
def findTargetSumWays(self, nums: list, target: int) -> int:
sums = sum(nums)
diff = sums - target
if diff < 0 or diff % 2 != 0:
return 0
n = len(nums)
neg = diff // 2
dp = [[0] * (neg + 1) for i in range(n+1)]
dp[0][0] = 1
for i in range(1, n+1):
num = nums[i-1]
for j in range(neg + 1):
dp[i][j] = dp[i -1][j]
if j >= num:
dp[i][j] += dp[i-1][j-num]
return dp[n][neg]
if __name__ == '__main__':
s = Solution()
print(s.findTargetSumWays([1,1,1,1,1], 3))
复杂度分析
时间复杂度:O(n×(sum−target))O(n \times (\textit{sum}-\textit{target}))O(n×(sum−target)),其中 n 是数组 nums 的长度,sum 是数组 nums 的元素和,target 是目标数。动态规划有$ (n+1) \times (\dfrac{\textit{sum}-\textit{target}}{2}+1)$ 个状态,需要计算每个状态的值。
空间复杂度:O(sum−target)O(\textit{sum}-\textit{target})O(sum−target),其中 sum 是数组 nums 的元素和,target 是目标数。使用空间优化的实现,需要创建长度为 sum−target2+1\dfrac{\textit{sum}-\textit{target}}{2}+12sum−target+1的数组 dp。