
不可以排序,然后*"一路加,超过就停"**(类似顺序贪心 + 提前终止):
python
// eg:"一条路径走到底,错了就结束"
nums = [1,2,3,4]
total = 10 → target = 5
排序后:[1,2,3,4]
1 → sum = 1
+2 → sum = 3
+3 → sum = 6 ❌ 超了 → 返回 False
但正确答案是 True
1 + 4 = 5 ✔
正确解法需要:"尝试不同组合"
| 方法 | 能否回退 | 能否尝试不同组合 |
|---|---|---|
| 贪心 | ❌ | ❌ |
| 回溯 / DP | ✅ | ✅ |
找一个子集,使它的和 = 总和的一半
回溯 / 暴力
问题本质:每个数都有两种选择:选 / 不选
时间复杂度:O(2^n)
👉 会超时
python
class Solution:
def canPartition(self, nums: List[int]) -> bool:
total = sum(nums)
# 总和是奇数 → 不可能平分:
if total % 2 != 0:
return False
# 子集和
target = total // 2
def dfs(i, cur_sum): # i:当前处理的idx
# 找到目标
if cur_sum == target:
return True
# 越界 or 超了
if i >= len(nums) or cur_sum > target:
return False
# 选择: 当前处理的idx+1, 当前和+nums[i]
# or 不选择: 当前处理的idx+1, 当前和不变
return dfs(i + 1, cur_sum + nums[i]) or dfs(i + 1, cur_sum)
# 从idx=0开始处理,当前和=0
return dfs(0, 0) # def1里写def2, def1调用def2直接def2
记忆化递归
python 缓存装饰器 @cache
复杂度从 O(2n)O(2^n)O(2n) 降到了 O(n⋅target)O(n \cdot \text{target})O(n⋅target)。
"索引 × 和" → O(n × target):状态组合最多只有 n×targetn \times \text{target}n×target 种,每个只算一次。这就是动态规划的递归版本!
python
class Solution:
def canPartition(self, nums: List[int]) -> bool:
total = sum(nums)
# 总和是奇数 → 不可能平分:
if total % 2 != 0:
return False
# 子集和
target = total // 2
@cache
def dfs(i, cur_sum): # i:当前处理的idx
# 找到目标
if cur_sum == target:
return True
# 越界 or 超了
if i >= len(nums) or cur_sum > target:
return False
# 选择: 当前处理的idx+1, 当前和+nums[i]
# or 不选择: 当前处理的idx+1, 当前和不变
return dfs(i + 1, cur_sum + nums[i]) or dfs(i + 1, cur_sum)
# 从idx=0开始处理,当前和=0
return dfs(0, 0) # def1里写def2, def1调用def2直接def2