LeetCode 494. 目标和 动态规划详解

494. 目标和

题目来源

494. 目标和

题目分析

给定一个非负整数数组 nums 和一个目标数 target,我们需要在数组中任意选取整数,添加符号 +-,使得运算结果等于 target。我们要计算所有可能的符号添加方法数。

题目难度

  • 难度:中等

题目标签

  • 标签:
    • 数组
    • 动态规划
    • 回溯

题目限制

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000

解题思路

这道题可以转化为经典的 01 背包问题:

  1. 问题转化:我们可以将数组 nums 分成两部分,分别为符号为 + 的部分和符号为 - 的部分。设 P 是符号为 + 的部分的和,S 是数组 nums 的和,则符号为 - 的部分的和为 S - P。题目要求 P - (S - P) = target,简化为 2P = S + target

  2. 约束条件:需要确保 S + target 是非负且为偶数,否则不存在满足条件的 P

  3. 动态规划:问题转化为一个 01 背包问题,背包容量为 (S + target) / 2,我们需要找到和为这个值的所有组合数。

核心算法步骤

  1. 初始化

    • 定义 dp[i] 为和为 i 的方法数。
    • 初始化 边界 dp[0] = 1,表示选择的和为 0 的情况有 1 种(不选任何数)。
  2. 状态转移

    • 对于数组中的每个数 num,我们有两种选择:
      • 不选择 num,则 dp[i] 保持不变。
      • 选择 num,则 dp[i] 需要加上 dp[i - num]
  3. 输出结果

    • 最终返回 dp[(S + target) / 2] 即为符合条件的方案数。

代码实现

以下是求解目标和问题的 Java 代码:

/**
 * 494. 目标和
 * @param nums   非负整数数组
 * @param target 目标数
 * @return 方法数
 */
public int findTargetSumWays(int[] nums, int target) {
    int n = nums.length;
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }
    if ((sum + target) % 2 != 0 || sum < Math.abs(target)) {
        return 0;
    }
    target = (sum + target) / 2;

    int[] dp = new int[target + 1];
    dp[0] = 1;
    for (int num : nums) {
        for (int i = target; i >= num; i--) {
            dp[i] += dp[i - num];
        }
    }
    return dp[target];
}

代码解读

  • findTargetSumWays 方法

    • 首先计算数组的总和 sum,并检查 sum + target 是否为偶数和非负。
    • 然后,将问题转化为背包容量为 (sum + target) / 2 的 01 背包问题,使用动态规划计算所有可能的组合数。
  • 滚动数组优化

    • 使用一维数组 dp 实现滚动数组优化,减少空间复杂度。

性能分析

  • 时间复杂度O(n * target),其中 n 是数组的长度,target 是转换后的背包容量。
  • 空间复杂度O(target),使用滚动数组进行空间优化。

测试用例

你可以使用以下测试用例来验证代码的正确性:

int[] nums1 = {1, 1, 1, 1, 1};
int result1 = findTargetSumWays(nums1, 3);
System.out.println(result1);
// 输出: 5 (共有 5 种方法使得表达式结果为 3)

int[] nums2 = {1, 2, 3, 4, 5};
int result2 = findTargetSumWays(nums2, 3);
System.out.println(result2);
// 输出: 3 (共有 3 种方法使得表达式结果为 3)

扩展讨论

优化写法

  • 空间优化
    • 使用一维数组 dp 进行空间优化,将原来的二维数组优化为一维滚动数组。
    • 其实选择添加正号或者选择添加负号都是可以的,设 S 是数组 nums 的和,设 P 是符号为 + 的部分的和,设 Q 是符号为 - 的部分的和,则S = Q + P。题目要求 P - Q = target,可以简化为 2P = S + target,也可以化简为2Q = S - target,这样把可以把Q 作为背包的容量,同样可以求出解。所以在target是正数时P会更小,反之Q更小,选择更小的数作为背包的容量可以减少循环的次数,减少时间复杂度,因此,将背包容量m = (S - |target|)/2更好。

其他实现

  • DFS + 记忆化
    • 除了动态规划,还可以使用深度优先搜索 (DFS) 加上记忆化搜索来解决这个问题,但对于大规模数据,效率可能不如动态规划。

总结

通过将问题转化为 01 背包问题,并结合动态规划技术,我们能够高效地解决目标和问题。动态规划的核心在于状态的定义和转移,是解决此类问题的有力工具。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值