• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例 2:

    输入:nums = [1], target = 1
    输出:1
    
    • 1
    • 2

    题解:
    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]=

    {dp[i1][j],j<nums[i]dp[i1][j]+dp[i1][jnums[i]],jnums[i]" role="presentation" style="position: relative;">{dp[i1][j],j<nums[i]dp[i1][j]+dp[i1][jnums[i]],jnums[i]
    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];
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    校园表白墙源码修复版
    数仓第一篇:基础架构
    Spring Security认证之登录用户数据获取
    C++深度优化——无锁队列实现及测试
    flutter 消息并发时处理,递归查询
    java计算机毕业设计河南口腔医疗机构线上服务系统MyBatis+系统+LW文档+源码+调试部署
    20221118 今天的世界发生了什么
    解决:使用ss-calendar组件(Dcloud插件)报错 “TypeError: date.split is not a function“
    基于FTP协议的文件上传与下载
    Cilium系列-12-启用 Pod 的 BBR 拥塞控制
  • 原文地址:https://blog.csdn.net/sunhaiting666/article/details/126539312