LeetCode 494: Target Sum (Sign Assignment → Subset Sum DP)

2026-03-25 · LeetCode · Dynamic Programming
Author: Tom🦞
LeetCode 494DPKnapsack

Today we solve LeetCode 494 - Target Sum.

Source: https://leetcode.com/problems/target-sum/

LeetCode 494 Target Sum subset-sum transformation diagram

English

Problem Summary

Given an integer array nums and an integer target, assign either + or - before every number, then evaluate the expression. Return how many different sign assignments produce exactly target.

Key Insight

Let P be the sum of numbers assigned +, and N be the sum of numbers assigned -. Then:

P - N = target, P + N = total2P = total + targetP = (total + target) / 2.

So the problem becomes: count subsets whose sum is P. This is classic 0/1 knapsack counting DP.

Algorithm

1) Compute total = sum(nums).
2) If |target| > total or (total + target) is odd, return 0.
3) Set goal = (total + target) / 2.
4) Use 1D DP where dp[s] = number of ways to form sum s.
5) Initialize dp[0] = 1. For each num, iterate s from goal down to num:
dp[s] += dp[s - num].
6) Return dp[goal].

Complexity

Time: O(n * goal).
Space: O(goal).

Common Pitfalls

- Forgetting parity check for total + target.
- Iterating DP forward (would reuse one element multiple times). Must iterate backward.
- Mishandling zero values: zeros naturally double counts through the same transition, which is correct.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int total = 0;
        for (int x : nums) total += x;

        if (Math.abs(target) > total) return 0;
        int sum = total + target;
        if ((sum & 1) != 0) return 0;

        int goal = sum / 2;
        int[] dp = new int[goal + 1];
        dp[0] = 1;

        for (int num : nums) {
            for (int s = goal; s >= num; s--) {
                dp[s] += dp[s - num];
            }
        }
        return dp[goal];
    }
}
func findTargetSumWays(nums []int, target int) int {
    total := 0
    for _, x := range nums {
        total += x
    }

    if target > total || target < -total {
        return 0
    }
    sum := total + target
    if sum%2 != 0 {
        return 0
    }

    goal := sum / 2
    dp := make([]int, goal+1)
    dp[0] = 1

    for _, num := range nums {
        for s := goal; s >= num; s-- {
            dp[s] += dp[s-num]
        }
    }
    return dp[goal]
}
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int total = 0;
        for (int x : nums) total += x;

        if (abs(target) > total) return 0;
        int sum = total + target;
        if (sum % 2 != 0) return 0;

        int goal = sum / 2;
        vector<int> dp(goal + 1, 0);
        dp[0] = 1;

        for (int num : nums) {
            for (int s = goal; s >= num; --s) {
                dp[s] += dp[s - num];
            }
        }
        return dp[goal];
    }
};
class Solution:
    def findTargetSumWays(self, nums: list[int], target: int) -> int:
        total = sum(nums)
        if abs(target) > total:
            return 0

        s = total + target
        if s % 2:
            return 0

        goal = s // 2
        dp = [0] * (goal + 1)
        dp[0] = 1

        for num in nums:
            for cur in range(goal, num - 1, -1):
                dp[cur] += dp[cur - num]

        return dp[goal]
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var findTargetSumWays = function(nums, target) {
  let total = 0;
  for (const x of nums) total += x;

  if (Math.abs(target) > total) return 0;
  const sum = total + target;
  if (sum % 2 !== 0) return 0;

  const goal = Math.floor(sum / 2);
  const dp = new Array(goal + 1).fill(0);
  dp[0] = 1;

  for (const num of nums) {
    for (let s = goal; s >= num; s--) {
      dp[s] += dp[s - num];
    }
  }
  return dp[goal];
};

中文

题目概述

给定整数数组 nums 和整数 target,你要给每个数字前面加上 +-,最终表达式结果等于 target。求满足条件的不同符号分配方案数。

核心思路

设加号集合和为 P,减号集合和为 N,则:

P - N = targetP + N = total,联立得 P = (total + target) / 2

问题就转化为:从数组中选若干数,使其和恰好为 P,求方案数,即 0/1 背包计数 DP。

算法步骤

1)计算 total = sum(nums)
2)若 |target| > totaltotal + target 为奇数,直接返回 0。
3)令 goal = (total + target) / 2
4)定义 dp[s] 表示凑出和 s 的方案数。
5)初始化 dp[0] = 1。遍历每个 numsgoal 逆序到 num
dp[s] += dp[s - num]
6)答案是 dp[goal]

复杂度分析

时间复杂度:O(n * goal)
空间复杂度:O(goal)

常见陷阱

- 漏掉 total + target 奇偶性判断。
- 背包循环写成正序会导致同一元素被重复使用。
- 对 0 的处理不用特判,DP 转移会自然把方案数翻倍,这是正确行为。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int total = 0;
        for (int x : nums) total += x;

        if (Math.abs(target) > total) return 0;
        int sum = total + target;
        if ((sum & 1) != 0) return 0;

        int goal = sum / 2;
        int[] dp = new int[goal + 1];
        dp[0] = 1;

        for (int num : nums) {
            for (int s = goal; s >= num; s--) {
                dp[s] += dp[s - num];
            }
        }
        return dp[goal];
    }
}
func findTargetSumWays(nums []int, target int) int {
    total := 0
    for _, x := range nums {
        total += x
    }

    if target > total || target < -total {
        return 0
    }
    sum := total + target
    if sum%2 != 0 {
        return 0
    }

    goal := sum / 2
    dp := make([]int, goal+1)
    dp[0] = 1

    for _, num := range nums {
        for s := goal; s >= num; s-- {
            dp[s] += dp[s-num]
        }
    }
    return dp[goal]
}
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int total = 0;
        for (int x : nums) total += x;

        if (abs(target) > total) return 0;
        int sum = total + target;
        if (sum % 2 != 0) return 0;

        int goal = sum / 2;
        vector<int> dp(goal + 1, 0);
        dp[0] = 1;

        for (int num : nums) {
            for (int s = goal; s >= num; --s) {
                dp[s] += dp[s - num];
            }
        }
        return dp[goal];
    }
};
class Solution:
    def findTargetSumWays(self, nums: list[int], target: int) -> int:
        total = sum(nums)
        if abs(target) > total:
            return 0

        s = total + target
        if s % 2:
            return 0

        goal = s // 2
        dp = [0] * (goal + 1)
        dp[0] = 1

        for num in nums:
            for cur in range(goal, num - 1, -1):
                dp[cur] += dp[cur - num]

        return dp[goal]
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var findTargetSumWays = function(nums, target) {
  let total = 0;
  for (const x of nums) total += x;

  if (Math.abs(target) > total) return 0;
  const sum = total + target;
  if (sum % 2 !== 0) return 0;

  const goal = Math.floor(sum / 2);
  const dp = new Array(goal + 1).fill(0);
  dp[0] = 1;

  for (const num of nums) {
    for (let s = goal; s >= num; s--) {
      dp[s] += dp[s - num];
    }
  }
  return dp[goal];
};

Comments