leetcode
leetcode 2951 ~ 3000
目标和

目标和

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 45 ms, 内存: 16.0 MB


/*
 * 题目思路:
 * 在Java中,我们可以使用Stream来处理这种动态规划问题。
 * 我们可以通过递归和Stream来求解所有可能的表达式数量。
 */

import java.util.stream.IntStream;

public class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = IntStream.of(nums).sum();
        if (Math.abs(target) > sum) return 0;
        int[] dp = new int[2 * sum + 1];
        dp[sum] = 1;
        for (int num : nums) {
            int[] next = new int[2 * sum + 1];
            IntStream.range(0, dp.length).filter(i -> dp[i] != 0).forEach(i -> {
                next[i + num] += dp[i];
                next[i - num] += dp[i];
            });
            dp = next;
        }
        return dp[sum + target];
    }
}

解释

方法:

这个题解使用了动态规划的方法解决问题。首先,通过转换问题,我们可以将问题转化为一个子集和问题。原问题是在每个数字前添加'+'或'-',使得最终的表达式结果等于target。我们可以将这个问题转化为,选择一部分数字使用'+',另一部分使用'-',使得这两部分的差等于target。设P为正子集,N为负子集,那么sum(P) - sum(N) = target。同时我们知道sum(P) + sum(N) = sum(nums),结合这两个等式,我们可以得到sum(P) = (target + sum(nums)) / 2。因此,问题转化为在nums中找到和为(sum(nums) + target) / 2的子集个数。使用动态规划数组dp,dp[i]表示和为i的子集个数。遍历每个数字,更新dp数组。最终dp[size]即为答案。

时间复杂度:

O(n*sum)

空间复杂度:

O(sum)

代码细节讲解

🦆
题解中提到`(target + sum(nums)) % 2 == 1`时返回0,能否解释为什么当这个表达式为奇数时,就没有可能的解?
这个条件是基于数学转换而来的。在原问题中,我们需要找到一种数字的正负标记方式,使得标记后的总和等于目标值 `target`。通过引入两个子集:P为正子集和N为负子集,我们有等式 `sum(P) - sum(N) = target` 和 `sum(P) + sum(N) = sum(nums)`。将这两个等式联立,可以得到 `sum(P) = (sum(nums) + target) / 2`。这个结果 `sum(P)` 必须是一个整数。如果 `sum(nums) + target` 是奇数,那么除以2后得到的结果不是整数,这意味着不存在这样的子集P,使得它们的和为一个非整数值。因此,当 `(sum(nums) + target) % 2 == 1`,即为奇数时,不可能有解。
🦆
在动态规划的实现中,为什么初始化`dp[0]`为1,而其他`dp[i]`为0?这样的初始化对算法的影响是什么?
在动态规划中,`dp[i]` 表示和为i的子集的数量。初始化 `dp[0]` 为1是因为和为0的子集只有一个,即空集。这是基本的边界条件,代表没有选择任何元素时,唯一达到和为0的方法是什么都不选。其他的 `dp[i]` 被初始化为0因为在开始时,没有其他和的子集被发现。这种初始化方式确保了动态规划的正确性,使得后续在更新 `dp` 数组时,每个 `dp[i]` 正确地反映了和为i的子集的数量。
🦆
题解中的动态规划算法是从`size`向下遍历到`num`,为什么要这样反向更新dp数组?直接正向更新有什么潜在的问题?
在动态规划中,反向更新 `dp` 数组是为了确保每个数字在每个阶段只被计算一次。如果我们正向更新dp数组,那么在计算 `dp[i]` 时,我们可能会使用同一个 `num` 更新多次,这会导致计算重复,因此会得到错误的结果。反向更新是为了确保当我们处理任何 `dp[i]` 时,依赖的是上一阶段(即还未更新本轮数字前)的 `dp` 数组的值。这样,每个数字对特定和的影响只被计算一次,保证了结果的正确性。

相关问题