2021-12-6 494. 目标和(动态规划)

注:

题目:
给你一个整数数组 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

题解:
思路
假设加法的总和为x,那么减法对应的总和就是sum - x。

所以我们要求的是 x - (sum - x) = target

x = (target + sum) / 2

此时问题就转化为,装满容量为x背包,有几种方法。

动态规划过程如下:
确定dp数组以及下标的含义
定义二维数组 dp,其中dp[i][j] 表示在数组nums[0]到nums[i]中选取元素,使得这些元素之和等于j的方案数。
假设数组 nums 的长度为 n,则最终答案为 dp[n-1][bagsize]。

确定递推公式
如果 j<num,则不能选 num,此时有 dp[i][j]=dp[i−1][j];

如果 j≥num,则如果不选 }num,方案数是dp[i−1][j],如果选 num,方案数是 dp[i−1][j−num],此时有dp[i][j]=dp[i−1][j]+dp[i−1][j−num]。
在这里插入图片描述

dp数组如何初始化
当没有任何元素可以选取时,元素和只能是 0,对应的方案数是 1,因此动态规划的边界条件是:
dp[0][0]=1

除此之外,当nums[0]==背包容量j时,可以选择nums[0]以满足背包容量的要求,即dp[0][j]=1

确定遍历顺序
先遍历物品,再遍历背包容量

复杂度分析
时间复杂度O(n * m),n为正数个数,m为背包容量
空间复杂度:O(n*m)

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(int i:nums){
            sum+=i;
        }
        if(sum+target<0||((sum+target)%2==1)){
            return 0;
        }
        int bagsize=(sum+target)/2;
        //dp[i][j]在数组nums[0]-nums[i]中选取元素,使得这些元素之和等于j的方案数
        vector<vector<int>> dp(nums.size(),vector<int>(bagsize+1,0));
        for(int j=0;j<=bagsize;j++){//初始化
            if(j==0){
                dp[0][j]=1;
            }
            if(nums[0]==j){
                dp[0][j]++;
            }
        }
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<=bagsize;j++){
                if(nums[i]>j){
                    dp[i][j]=dp[i-1][j];
                }
                else{
                    dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]];
                }
            }
        }
        return dp[nums.size()-1][bagsize];
    }
};

优化空间复杂度至O(m):

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(int i:nums){
            sum+=i;
        }
        if(sum+target<0||((sum+target)%2==1)){
            return 0;
        }
        int bagsize=(sum+target)/2;
        //dp[i][j]在数组nums[0]-nums[i]中选取元素,使得这些元素之和等于j的方案数
        vector<int> dp(bagsize+1,0);
        for(int j=0;j<=bagsize;j++){
            if(j==0){
                dp[0]=1;
            }
            if(nums[0]==j){
                dp[j]++;
            }
        }
        for(int i=1;i<nums.size();i++){
            for(int j=bagsize;j>=nums[i];j--){
                if(nums[i]>j){
                    dp[j]=dp[j];
                }
                else{
                    dp[j]=dp[j]+dp[j-nums[i]];
                }
            }
        }
        return dp[bagsize];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值