Leetcode494. 目标和

Leetcode494. 目标和

相似题目:
Leetcode416. 分割等和子集
题目:
给你一个整数数组 n u m s nums nums 和一个整数 t a r g e t target target
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如, n u m s = [ 2 , 1 ] nums = [2, 1] nums=[2,1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 t a r g e t target 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

题解:
0-1背包问题
记数组的元素和为 sum \textit{sum} sum,添加 - \texttt{-} - 号的元素之和为 neg \textit{neg} neg,则其余添加 + \texttt{+} + 的元素之和为 sum − neg \textit{sum}-\textit{neg} sumneg,得到的表达式的结果为
( sum − neg ) − neg = sum − 2 ⋅ neg = target ( s u m − n e g ) − n e g = s u m − 2 ⋅ n e g = t a r g e t (\textit{sum}-\textit{neg})-\textit{neg}=\textit{sum}-2\cdot\textit{neg}=\textit{target} (sum−neg)−neg=sum−2⋅neg=target (sumneg)neg=sum2neg=target(sumneg)neg=sum2neg=target

neg = sum − target 2 \textit{neg}=\dfrac{\textit{sum}-\textit{target}}{2} neg=2sumtarget

由于数组 nums \textit{nums} nums 中的元素都是非负整数, neg \textit{neg} neg 也必须是非负整数,所以上式成立的前提是 sum − target \textit{sum}-\textit{target} sumtarget是非负偶数。若不符合该条件可直接返回 0。

若上式成立,问题转化成在数组 nums \textit{nums} nums 中选取若干元素,使得这些元素之和等于 neg \textit{neg} neg,计算选取元素的方案数。我们可以使用动态规划的方法求解。

定义二维数组 dp \textit{dp} dp,其中 dp [ i ] [ j ] \textit{dp}[i][j] dp[i][j] 表示从数组的 [ 0 , i ] [ 0 , i ] [0,i]下标范围内选取若干个正整数,使得这些元素之和等于 j j j 的方案数。假设数组 nums \textit{nums} nums 的长度为 n,则最终答案为 dp [ n − 1 ] [ neg ] \textit{dp}[n-1][\textit{neg}] dp[n1][neg]

在定义状态之后,需要考虑边界情况。以下两种情况都属于边界情况。

  • 如果不选取任何正整数,则被选取的正整数等于 0。因此对于所有 0 ≤ i < n 0≤i < n 0i<n,都有 d p [ i ] [ 0 ] = 1 dp [ i ] [ 0 ] = 1 dp[i][0]=1
  • i = 0 i = 0 i=0 时,只有一个正整数 nums [ 0 ] \textit{nums}[0] nums[0]可以被选取,因此 d p [ 0 ] [ n u m s [ 0 ] ] = 1 dp [ 0 ] [ nums [ 0 ]] = 1 dp[0][nums[0]]=1
  • 当没有任何元素可以选取时, d p [ 0 ] [ j ] = 0 ( j > 0 ) dp[0][j]=0(j>0) dp[0][j]=0(j>0)

1 ≤ i < n 1 \le i < n 1i<n时,对于数组 nums \textit{nums} nums中的第 i i i 个元素 num \textit{num} num(i 的计数从 1 开始),遍历 0 ≤ j ≤ neg 0 \le j \le \textit{neg} 0jneg,计算 dp [ i ] [ j ] \textit{dp}[i][j] dp[i][j] 的值:

如果 j < num j < \textit{num} j<num,则不能选 num \textit{num} num,此时有 dp [ i ] [ j ] = dp [ i − 1 ] [ j ] \textit{dp}[i][j] = \textit{dp}[i - 1][j] dp[i][j]=dp[i1][j]

如果 j ≥ num j \ge \textit{num} jnum,则如果不选 num \textit{num} num,方案数是 dp [ i − 1 ] [ j ] \textit{dp}[i - 1][j] dp[i1][j],如果选 num \textit{num} num,方案数是 dp [ i − 1 ] [ j − num ] \textit{dp}[i - 1][j - \textit{num}] dp[i1][jnum],此时有 dp [ i ] [ j ] = dp [ i − 1 ] [ j ] + dp [ i − 1 ] [ j − num ] \textit{dp}[i][j] = \textit{dp}[i - 1][j] + \textit{dp}[i - 1][j - \textit{num}] dp[i][j]=dp[i1][j]+dp[i1][jnum]

因此状态转移方程如下:

dp [ i ] [ j ] = { dp [ i − 1 ] [ j ] , j < nums [ i ] dp [ i − 1 ] [ j ] + dp [ i − 1 ] [ j − nums [ i ] ] , j ≥ nums [ i ] \textit{dp}[i][j]=\begin{cases} \textit{dp}[i - 1][j], & j<\textit{nums}[i] \\ \textit{dp}[i - 1][j] + \textit{dp}[i - 1][j - \textit{nums}[i]], & j \ge \textit{nums}[i] \end{cases} dp[i][j]={dp[i1][j],dp[i1][j]+dp[i1][jnums[i]],j<nums[i]jnums[i]

最终得到 dp [ n − 1 ] [ neg ] \textit{dp}[n-1][\textit{neg}] dp[n1][neg] 的值即为答案。

java代码:

  /**
     * @param nums
     * @param target
     * @return
     */
    public static int findTargetSumWays(int[] nums, int target) {
        int len = nums.length;
        int sum = 0;
        for (int i = 0; i < len; i++) {
            sum += nums[i];
        }

        if ((sum - target) % 2 != 0) {
            return 0;
        }

        int neg = (sum - target) / 2;
        //dp[i][j] :在数组[0,i]中选取一些数,使得和为j
        int[][] dp = new int[len][neg + 1];

        //i=0时,只有一个正数nums[0]可以被选取
        dp[0][nums[0]] = 1;

        for (int i = 0; i < len; i++) {
            //从[0,i]中不选任何正整数
            dp[i][0] = 1;
        }

        for (int i = 1; i < len; i++) {
            for (int j = 1; j <= neg; j++) {
                if (j < nums[i]) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
                }
            }
        }
        return dp[len - 1][neg];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值