494. 目标和

本文解析了一道关于整数数组和目标和的编程题,通过回溯和动态规划两种方法解决。回溯用于枚举正负符号,动态规划则将问题转化为背包问题,通过状态转移方程求解不同表达式数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

        这道题我只想到了回溯,因为真的很适合回溯。。。

        对于每个元素,先选取其负数表示,判断相加的和是否为target;然后回溯,去掉该元素,加上该元素的正数表示,再次判断相加和是否为target。老生常谈了,就不细说了。

        代码如下:

class Solution {
public:
    int res=0;
    int findTargetSumWays(vector<int>& nums, int target) {
        dfs(nums,target,0,0);
        return res;
    }
    // dfs功能:对nums数组中的每一个元素,选取它的正值和负值,依次进行计算
    void dfs(vector<int>& nums, int target,int index,int sum)
    {
        int len=nums.size();
        // 长度越界,返回
        if(index>len) return;
        if(index==len)
        {
            // 如果访问到最后一个元素,和刚好为target,则res++
            if(sum==target) res++;
            return;
        } 
        // 依次选取正数与负数
        for(int i=-1;i<=1;i=i+2)
        {
            int tempnum=i*nums[index];
            // sum加上该元素
            sum+=tempnum;
            // 递归访问下一元素
            dfs(nums,target,index+1,sum);
            // 回溯,从sum中去除该元素,选取另一个符号进行操作
            sum-=tempnum;
        }
        return;
    }

};

        但是看了看题解,还有动归的做法,以及题解的思路如果没见过,一般是想不出来的。背包问题是选取与不选取的问题,但如果是这道题,是选择正数还是负数的问题,表面上看是不符合的。但是,题解进行了转化。设前面添加-号的元素的和为neg,前面添加+号的元素的和为pos,nums数组的和为sum,可以知道,pos-neg=target=(sum-neg )-neg=sum-2*neg

        即:neg=(sum-target)/2

        前提条件:

        1.sum-target要大于0,如果sum-target<0,直接返回0,因为nums中全为正数

        2.(sum-target)/2必须为偶数,否则返回0

        因此,题目就变成了,从nums数组中,选取若干元素,构成和为neg的方案总数,每一种元素都面临选或者不选两种方式,这就转化为了典型的背包问题。

        设dp[i][j]为从0~i个元素中,选取若干个,构成和为j的方案总数。初始化所有dp元素为0。

        初始情况下,dp[0][0]+=1,因为不选取第0个元素的前提下,构成和为0的方案数为1;dp[0][nums[0]]+=1,在选取第0个元素的前提下,构成和为nums[0]的方案数为1;

        之后按照递推公式进行递推即可:

        对每一个dp[i][j],有:

        1.不选取第i个元素:dp[i][j]+=dp[i-1][j]

        2.选取第i个元素:dp[i][j]+=dp[i-1][j-nums[i]]

详细分析请看题解:

https://leetcode-cn.com/problems/target-sum/solution/mu-biao-he-by-leetcode-solution-o0cp/

代码如下:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int n=nums.size();
        int sum=0;
        // 计算数组和sum
        for(int i=0;i<n;i++)
        sum+=nums[i];
        // 如果sum-target为负数或者neg为奇数,直接返回0
        if(sum-target<0||(sum-target)%2==1) return 0;
        // 题目转化为求解在nums数组中,寻找若干元素,他们的和为neg
        int neg=(sum-target)/2;
        // dp[i][j]代表0~i个元素组成和为j的个数
        // 不选取该元素:dp[i][j]=dp[i-1][j];
        // 选取该元素:dp[i][j]=dp[i-1][j-nums[i]];
        int dp[n][1005];
        memset(dp,0,sizeof(dp));
        // 不选取第0个元素,构成和为j的方法数为1
        dp[0][0]+=1;
        // 选取第0个元素,构成和为nums[0]的方法数为1
        dp[0][nums[0]]+=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=neg;j++)
            {
                // 利用递推方程求解dp[i][j]
                dp[i][j]+=dp[i-1][j];
                if(j-nums[i]>=0)
                dp[i][j]+=dp[i-1][j-nums[i]];
            }
        }
       
        return dp[n-1][neg];


    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值