LeetCode0494.目标和 Go语言AC笔记

 时间复杂度:O(n²),空间复杂度:O(n)

解题思路

动态规划之01背包。

首先我们先导一下公式。令正数和为sumP,负数和为sumN,数组元素总和为sum,由题意可知

  1. sumP+sumN=target
  2. sumP-sumN=sum

 所以①+②可得2*sumP=target+sum即sumP=(target+sum)/2

这样我们就可以把该题转化为01背包,在数组中选几个元素凑成(target+sum)/2的和,和《416.分割等和子集》题思路相仿,不过该题dp[i][j]的含义为前i个元素的和为j的方案数。

这样当我们遍历数组时对于每个元素都有两种情况——选择它成为正数和不选择它让它当负数,需要注意的是,如果元素num>j,那么我们不能选择它成为正数,因为如果选择它会使前i个元素的和超过j,不满足题意;而对于num<j的元素,如果我们选择它,那么dp[i][j]=dp[i-1][j-nums[i]],相当于选择它的方案数就等同于前i-1个元素凑出和为j-nums[i]的方案数,如果是不选择它,dp[i][j]=dp[i-1][j],方案数就是前i-1个元素和为j的方案数,最后dp[i][j]就是这两种情况方案数的总和。

综上,状态转移方程为

  • dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]] (j≥nums[i])
  • dp[i][j]=dp[i-1][j](j<nums[i])

我们可以发现dp[i][j]的状态均依赖于dp[i-1]的状态,所以我们可以用滚动数组的方法将二维数组压缩成一维数组 ,空间复杂度降为O(n)。但是,接下来在更新dp时我们必须从大到小更新,这样不会造成我们依赖的状态已经被修改的情况发生。

这样状态转移方程就变成了

  • dp[j]=dp[j]+dp[j-nums[i]] (j≥nums[i])
  • dp[j]=dp[j](j<nums[i])

AC代码

func findTargetSumWays(nums []int, target int) int {
    //正数和为(target+数组总和)/2
    s:=0
    for _,num:=range nums{
        s+=num
    }
    if s<target||s+target<0||(s+target)%2==1{
        return 0
    }
    sumP:=(s+target)/2
    dp:=make([]int,sumP+1)
    dp[0]=1
    for _,num:=range nums{
        for j:=sumP;j>=num;j--{
            dp[j]+=dp[j-num]
        }
    }
    return dp[sumP]
}

感悟

这次终于可以正确想到动态规划的思路了,主要原因是昨天做的题目就是“分割等和子集”题,对01背包的记忆更深刻一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SwithunH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值