LeetCode 494:目标和(Target Sum)------ 题解 ✅
🔗 题目链接
👉 https://leetcode.cn/problems/target-sum/
📖 内容概要
给定一个非负整数数组 nums 和一个整数 target,
你可以在每个数字前加 + 或 -,使计算结果等于 target。
返回所有可能的表达式数量。
✅ 0/1 背包(计数问题)
✅ 数学转化是关键
✅ 面试高频题
💡 解题思路(核心)
一、关键数学转化(非常重要)
设:
- 正数和为
P - 负数绝对值和为
N
则有:
P - N = target
P + N = sum
解得:
P = (sum + target) / 2
👉 问题转化为:
从数组中选出若干数,使其和正好等于
P
二、不可行的情况(剪枝)
| 情况 | 原因 |
|---|---|
(sum + target) 为奇数 |
无法整除 |
| ` | target |
java
if ((sum + target) % 2 == 1) return 0;
if (Math.abs(target) > sum) return 0;
三、DP 定义
java
dp[j] = 和为 j 的方案数
四、状态转移方程
java
dp[j] += dp[j - nums[i]];
✅ 每个数只能用一次
✅ 倒序遍历(0/1 背包)
五、初始化
java
dp[0] = 1;
什么都不选,是一种方案
✅ AC 代码(Java)
java
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int a : nums) {
sum += a;
}
int left = (sum + target) / 2;
// 剪枝
if ((sum + target) % 2 == 1) return 0;
if (Math.abs(target) > sum) return 0;
int[] dp = new int[left + 1];
dp[0] = 1;
// 0/1 背包(计数)
for (int i = 0; i < nums.length; i++) {
for (int j = left; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[left];
}
}
⏱️ 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(n × sum) |
| 空间复杂度 | O(sum) |
🔍 与 416 / 1049 的对比
| 题目 | 目标 |
|---|---|
| 416. 分割等和子集 | 是否存在 |
| 1049. 最后一块石头 II | 最小差值 |
| 494. 目标和 | 方案数 |
✅ 三题本质都是 子集和问题
✅ 区别在于 DP 含义不同
✅ 一句话总结
把"加减符号问题"转化为"子集和为 P 的方案数问题",再用 0/1 背包计数。
📌 面试加分点(建议记住)
- ✅ 为什么是
(sum + target) / 2 - ✅ 为什么
dp[0] = 1 - ✅ 为什么是
+=而不是max - ✅ 与回溯法的对比(会超时)