动态规划_11(LeetCode:494. 目标和)


一、题目

494. 目标和
在这里插入图片描述

二、题解

2.0_思路(写给自己看的)

1^这一个是错的
要求解表达式数目,则将dp数组含义先看为表达式值为target的不同数量
增加了一个新的数(遍历nums数组循环进入下一层)想要得到表达式值为target的组合种数
可以由target-这个数的组合数加上target+这个数的组合数

dp[target]=dp[target-nums[i]]+dp[target+nums[i]];

2^上面出现了重复计算
参考了代码随想录的方法

对于目标值target,得到target的式子可以写为
(a+b+c+…) - (x+y+z+…)=target
左部分(left)使用加号,右部分(right)减号
又因为left-right=sum;(sum已知)
所以原式可以写成:
left-(sum-left)=target
即:left=(target+sum)/2

最终问题就可以转为求nums数组中相加为( target+sum)/2的组合数

之前的问题是看能不能,这里是求组合数
所以要改变一下递推公式

2.1_动态规划

2.1.1_dp数组含义

一开始我的想法是对于target可以从(target-x)+和(target+x)-x两个方向计算target的组合数写完测试了一下,会出现重复计算的情况,而且边界情况不好处理。
后来参考了代码随想录的解法
问题转为求nums数组中和为(target+sum)/2的组合数
推导过程:

将能够组合成target的情况归纳成left(a+b+c+…)减去right(e+f+g+…)
又因为left+right=sum(nums数组的总和)
最后可以总结成left=(target+sum)/2

dp[i][j]含义:
nums数组中前i个数,和为j的组合数为dp[i] [j]

2.1.2_递推公式

之前的背包问题只要看装入的情况能不能满足条件
现在是求解组合数
所以递推公式改为:

// 注意是+=
dp[j]+=dp[j-num[i]]

2.1.3_初始化

第0层fori循环对应0个数字可以选择
目标值为0(dp[0])时,不放入物品也是一种情况
所以dp[0]=1,而其他初始化为0

2.1.4_deBug&完整代码

deBug:
1^ nums={1},target=2
因为nums数组(全部为非负数)的总和小于target,无论怎么组合都无法变成target

	// 当全部数(题目说明了全为非负数)相加还是小于target时,不存在方案
	if (sum < target)
		return 0;

2^ nums={7,9,3,8,0,2,4,8,3,9},target=0
当sum与target的奇偶性不同时,也是无论怎么组合都无法得到target

// 当sum和target奇偶性不同时(奇数加偶数结果为奇数,%2=1),不存在方案
if((sum+target)%2==1)
	return 0; 

3^ nums={100},target=-200
跟1^相同,也是target过大的情况
对1^优化一下:

	// 当全部数(题目说明了全为非负数)相加还是小于target的绝对值时,不存在方案
	if (sum < abs(target))
		return 0;

完整代码:

int findTargetSumWays(vector<int>& nums, int target) {
	int sum = accumulate(nums.begin(), nums.end(), 0);
	
	// 当全部数(题目说明了全为非负数)相加还是小于target时,不存在方案
	if (sum < target)
		return 0;
	// 当sum和target奇偶性不同时(奇数加偶数结果为奇数,%2=1),不存在方案
	if ((sum + target) % 2 == 1)
		return 0;
	int newT = (sum + target) / 2;
	vector<int> dp(newT+1, 0);
	// 背包容量为0时,不放入数也是一种方案
	dp[0] = 1;
	
	// 递推公式遍历dp数组
	for (int num_i : nums)
		for (int j = newT; j >= num_i; j--)
			dp[j] += dp[j - num_i];
	return dp[newT];
}

总结

总是找不到转成背包问题的思路,多练几题找找感觉

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值