目标和-动态规划

给你一个整数数组 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−negsumneg,得到的表达式的结果为

(sum−neg)−neg=sum−2∗neg=target(sum-neg) - neg = sum - 2*neg=target(sumneg)neg=sum2neg=target

neg=sum−target2neg = \frac{sum-target}{2}neg=2sumtarget

由于数组 numsnumsnums 中的元素都是非负整数,negnegneg 也必须是非负整数,所以上式成立的前提是 sum−targetsum−targetsumtarget非负偶数。若不符合该条件可直接返回 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≤n1in 时,对于数组 numsnumsnums 中的第 i 个元素 numnumnum(i 的计数从 1 开始),遍历 0≤j≤neg0≤j≤neg0jneg,计算 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[i1][j]

如果 j≥numj≥numjnum,则如果不选 numnumnum,方案数是 dp[i−1][j]dp[i-1][j]dp[i1][j],如果选 numnumnum,方案数是 dp[i−1][j−num]dp[i-1][j−num]dp[i1][jnum],此时有 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[i1][j]+dp[i1][jnum]

因此状态转移方程如下:

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[i1][j],j<numsdp[i1][j]+dp[i1][jnum],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×(sumtarget)),其中 n 是数组 nums 的长度,sum 是数组 nums 的元素和,target 是目标数。动态规划有$ (n+1) \times (\dfrac{\textit{sum}-\textit{target}}{2}+1)$ 个状态,需要计算每个状态的值。

空间复杂度:O(sum−target)O(\textit{sum}-\textit{target})O(sumtarget),其中 sum 是数组 nums 的元素和,target 是目标数。使用空间优化的实现,需要创建长度为 sum−target2+1\dfrac{\textit{sum}-\textit{target}}{2}+12sumtarget+1的数组 dp。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值