zoukankan      html  css  js  c++  java
  • 494. 目标和

    494. 目标和

    问题描述

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

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

    示例:

    输入:nums: [1, 1, 1, 1, 1], S: 3
    输出:5
    解释:
    
    -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
    
    一共有5种方法让最终目标和为3。
    

    提示:

    数组非空,且长度不会超过 20 。
    初始的数组的和不会超过 1000 。
    保证返回的最终结果能被 32 位整数存下。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/target-sum

    解答

    这题是在练习01背包找的题目,先入为主的就拿背包问题来做:

    [f(i, j) = f(i-1, j - num[i]) + f(i - 1, j + num[i]) ]

    (f(i, j ))是状态转移方程,将i个物品放入空间为j的背包的次数,因为可以存在加减法,所以为上述式子,但是在计算(f(i, j))(f(i-1, j+num[i]))是没有计算出来的。在查看别人的计算方法中,还想仍然有这样可以做出来的 ,但是这里就不考虑了。

    正确且方便的方法是,先观察题目,如果相加的数字和为p,相减的数字和为m,那么可以得到

    [p + m = sum(nums) \ p - m = S \ herefore p = frac{sum(nums) + S}{2} ]

    所以只需要在所有数字中,找到部分数字,让其和为 (frac{sum(nums) +S}{2}),这才是一个典型的01背包的问题:

    [f(i, j) = f(i - 1, j) + f(i - 1, j - nums[i]) ]

    (f(i, j))表示前 i 个物品装入空间为 j 的背包可能的数目。

    python 代码如下,这里需要注意的是,在初始化 dp 数组时,dp[0][0]为 1, 表示将 0 个物品放入 0 的背包的可能的次数为 1,并且其他行的第 0 个都是类似的情况。但下面就需要注意了,在传统的01背包中,第二个循环是从 1 开始的,但是这里因为可能的存在数字 0,所以我们必须从 0 开始。

    from typing import List
    class Solution:
        def findTargetSumWays(self, nums: List[int], S: int) -> int:
            N = len(nums)
            nums = [0, ] + nums
    
            # 判断可能性
            p, r = divmod(sum(nums) + S, 2)
            if r != 0 or abs(S) > sum(nums):
                return 0
    
            # dp 数组
            dp = [[1] + [0] * p for _ in range(N + 1)]
    
            for i in range(1, N + 1):
                for j in range(p + 1):
                    if j - nums[i] >= 0:
                        dp[i][j] = dp[i-1][j] + dp[i-1][j - nums[i]]
                    else:
                        dp[i][j] = dp[i-1][j]
    
            return dp[-1][-1]
    

    当然也可以进行空间的优化,注意第二个循环时反向的:

    from typing import List
    class Solution:
        def findTargetSumWays(self, nums: List[int], S: int) -> int:
            N = len(nums)
            nums = [0, ] + nums
    
            # 判断可能性
            p, r = divmod(sum(nums) + S, 2)
            if r != 0 or abs(S) > sum(nums):
                return 0
    
            # dp 数组
            dp = [1] + [0] * p
    
            for i in range(1, N + 1):
                for j in range(p, 0-1, -1):
                    if j - nums[i] >= 0:
                        dp[j] = dp[j] + dp[j - nums[i]]
    
            return dp[-1]
    

    总结

    1. 观察题目,这样的题目可能能够推导出更加简单的公式。
    2. 注意01背包的初始化以及循环,针对不同的题目,可能会存在不同的情况。
  • 相关阅读:
    如何在Android Studio中添加注释模板信息?
    Android Activity标签属性
    Android Activity全面解析
    Mac office ppt无法正常输入文字的问题解决方案
    将Android Studio默认布局ConstraintLayout切换成LinearLayout
    Java中Double保留后小数位的几种方法
    java文件传输接口
    纯JS编写打地鼠游戏
    JavaScript监听手机物理返回键的两种解决方法
    spring注解方式实现定时器
  • 原文地址:https://www.cnblogs.com/XD00/p/14534135.html
Copyright © 2011-2022 走看看