目标和-背包dp

发布于:2025-08-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

494. 目标和 - 力扣(LeetCode)

Solution

多种方法;

1.普通递归搜索

2.带缓存表的递归

3.动态规划+平移技巧

4.转化为01背包问题

#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;

class Solution {
public:
	unordered_map<int, unordered_map<int, int>> mp;
	int findTargetSumWays(vector<int>& nums, int target) {
		return f3(nums, target);
	}

	//直接递归解法
	int f1(vector<int>& nums, int target, int i, int s) {
		int n = nums.size();
		if (i == n) return s == target ? 1 : 0;
		return f1(nums, target, i + 1, s + nums[i]) + f1(nums, target, i + 1, s - nums[i]);
	}

	//带缓存表的递归
	int f2(vector<int>& nums, int target, int i, int s, unordered_map<int, unordered_map<int, int>>& mp) {
		int n = nums.size();
		if (mp.count(i) && mp[i].count(s)) return mp[i][s];
		if (i == n) return s == target ? 1 : 0;
		int ans = f2(nums, target, i + 1, s + nums[i], mp) + f2(nums, target, i + 1, s - nums[i], mp);
		mp[i][s] = ans;
		return ans;
	}

	//动态规划
	// 新思路,转化为01背包问题
	// 思考1:
	// 虽然题目说nums是非负数组,但即使nums中有负数比如[3,-4,2]
	// 因为能在每个数前面用+或者-号
	// 所以[3,-4,2]其实和[3,4,2]会达成一样的结果
	// 所以即使nums中有负数,也可以把负数直接变成正数,也不会影响结果
	// 思考2:
	// 如果nums都是非负数,并且所有数的累加和是sum
	// 那么如果target>sum,很明显没有任何方法可以达到target,可以直接返回0
	// 思考3:
	// nums内部的数组,不管怎么+和-,最终的结果都一定不会改变奇偶性
	// 所以,如果所有数的累加和是sum,并且与target的奇偶性不一样
	// 那么没有任何方法可以达到target,可以直接返回0
	// 思考4(最重要):
	// 比如说给定一个数组, nums = [1, 2, 3, 4, 5] 并且 target = 3
	// 其中一个方案是 : +1 -2 +3 -4 +5 = 3
	// 该方案中取了正的集合为A = {1,3,5}
	// 该方案中取了负的集合为B = {2,4}
	// 所以任何一种方案,都一定有 sum(A) - sum(B) = target
	// 现在我们来处理一下这个等式,把左右两边都加上sum(A) + sum(B),那么就会变成如下:
	// sum(A) - sum(B) + sum(A) + sum(B) = target + sum(A) + sum(B)
	// 2 * sum(A) = target + 数组所有数的累加和
	// sum(A) = (target + 数组所有数的累加和) / 2
	// 也就是说,任何一个集合,只要累加和是(target + 数组所有数的累加和) / 2
	// 那么就一定对应一种target的方式
	// 比如非负数组nums,target = 1, nums所有数累加和是11
	// 求有多少方法组成1,其实就是求,有多少种子集累加和达到6的方法,(1+11)/2=6
	// 因为,子集累加和6 - 另一半的子集累加和5 = 1(target)
	// 所以有多少个累加和为6的不同集合,就代表有多少个target==1的表达式数量
	// 至此已经转化为01背包问题了
	int f3(vector<int>& nums, int target) {
		int n = nums.size();
		int s = 0;
		for (int i = 0; i < n; ++i) {
			if (nums[i] < 0) nums[i] = -nums[i];
			s += nums[i];
		}
		if (target > s || -target > s) return 0;
		if ((s + target) % 2) return 0;
		int t = (s + target) / 2;
		vector<int>dp(t + 1, 0);
		dp[0] = 1;
		for (int i = 0; i < n; ++i) {
			for (int j = t; j >= nums[i]; --j) {
				dp[j] = dp[j] + dp[j - nums[i]];
			}
		}
		return dp[t];
	}
};

int main() {

	return 0;
}