题目
给你一个整数数组 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
思路和代码
唉,就完全没思路嘛!动态规划,那dp[i]表示值为i的表达式种类数,那dp[i]能有什么推导出来呢?和dp[i-1]有什么关系呢?dp[0]为0?
看了题解,刚开始有点绕,差点没绕过来。吃过饭一看,懂了。
例如nums=[1,1,1,1,1],里面的每个1在表达式中可能是+1,也可能是-1。首先这个数组的和为sum_value=sum(nums),那么假设在表达式中正数的和为x,那么负数的和为-(sum_value-x)。又因为正数的和+负数的和=target。所以就有x-(sum_value-x)=target,也就是x=(target+sum_value)/2。
怎么是一个背包问题呢?这里面的容量就是x,物品就是数组nums里面的数,而且每一个数只能放一次。那怎么解决每个数可能是正可能是负的问题呢?因为x是正数的和,所以只要考虑若干个整数把容量x装满即可。
首先有两种情况直接返回0。一种情况是如果x不是整数,另一种情况是target的绝对值大于sum_value。所以这也保证了bagsize肯定是大于等于0的。
如何推导得到dp[i],以dp[4]为例。
有一个1的话,有dp[3]种方法凑成dp[4]
有一个2的话,有dp[2]种方法凑成dp[4]
有一个3的话,有dp[1]种方法凑成dp[4]
有一个4的话,有dp[0]中方法凑成dp[4]
根据这个例子也可以看出来,dp[0]的初始化应该为1。如果dp[0]为0,后面的全都为0了。
综上,代码如下:
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
sum_value = sum(nums)
if (target + sum_value)%2 or abs(target) > sum_value:
return 0
bagsize = (target + sum_value) // 2
dp = [0] * (bagsize + 1)
dp[0] = 1
for i in range(len(nums)):
for j in range(bagsize,nums[i]-1,-1):
dp[j] += dp[j-nums[i]]
return dp[bagsize]