leetcode 494.目标和(DP 动态规划)

leetcode 494.目标和

题目来源:https://leetcode-cn.com/problems/target-sum/

题目

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

思路

本题是典型的“动态规划”问题。设数组的长度为n,

  1. 当数组的前n-1项(nums[0]到nums[n-2])按照题中的要求得出了S-nums[n-1],则最后“+nums[n-1]”就满足了题中要求,方法数加1。
  2. 或者数组的前n-1项(nums[0]到nums[n-2])按照题中的要求得出了S+nums[n-1],则最后“-nums[n-1]”也满足了题中要求,方法数加1。

dp()findTargetSumWays()的意义相同,vector<int> temp为当前vector<int> nums除最后一项外剩下的元素组成的数组、lennums的长度。

所以可以得出“状态转移公式”: d p ( n u m s , S ) = d p ( t e m p , S − n u m s [ l e n − 1 ] ) + d p ( t e m p , S + n u m s [ l e n − 1 ] ) dp(nums,S) = dp(temp,S-nums[len-1])+dp(temp, S+nums[len-1]) dp(nums,S)=dp(temp,Snums[len1])+dp(temp,S+nums[len1])
普通的动态规划递归函数会超时,在这里不再说明。接下来用题中的“示例1”来说明使用“二维数组”来解题的方法。

表格中横坐标表示从前到后扫描数组,当前扫描到的数组元素。纵坐标表示的是此时的目标数。对应的表格内容则是对应的方法数。
如表格中的(nums[2] = 1,-1)= 3表示的是扫描到数组的第三个元素时,组合得到目标数S = -3的方法有3种。
在这里插入图片描述
从表格种能够得到(nums[4] = 1,3)= 5!
并且表格中-S和S对应的方法数是相同的,这样可以进一步缩短“二维数组”的规模。

构造数组:

  1. 初始化二维数组的所有元素都为“0”。把第一行中与数组第一个元素相等的列数对应的表格置为“1”。(这里要注意的是,原本也要标记-S的情况,由于上述的原因而省略了,但如果S = 0,则-S = S,对应的位置就要标记两次在这里插入图片描述
  2. 接下来从第一行开始循环,扫描每一个元素,扫描到 ( i , j ) (i,j) (i,j)是非零元素时,其 ( i + 1 , j ± n u m s [ i + 1 ] 的 绝 对 值 ) (i+1,j\pm nums[i+1]的绝对值) (i+1,j±nums[i+1])要加上 ( i , j ) (i,j) (i,j)对应的值。(扫描每一行之后别忘了把S=0对应的表格元素本身 × 2 \times2 ×2在这里插入图片描述
  3. 最后一行,S的绝对值列对应的表格元素就是本题要求的方法数

C++代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
    	// 如果目标数大于初始的数组的和则方法数为0
        if (S > 1000) {
            return 0;
        }
        int dp[20][1001];
        int len = nums.size();
        // 初始化dp数组
        memset(dp, 0, sizeof(dp));
        dp[0][nums[0]] = 1;
        dp[0][0] *= 2;
        for (int i = 0; i < len - 1; i++) {
            for (int j = 0; j <= 1000; j++) {
                if (dp[i][j]) {
                	// 如果j=0时只加一次即可
                    if (j) {
                        dp[i + 1][abs(j - nums[i + 1])] += dp[i][j];
                    }
                    dp[i + 1][j + nums[i + 1]] += dp[i][j];
                }
            }
            // 每一行之后别忘了把S=0对应的表格元素本身*2
            dp[i + 1][0] *= 2;
        }
        return dp[len - 1][abs(S)];
    }
};

发现问题欢迎指出和纠正,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值