力扣:494.目标和

一、题目描述

给你一个整数数组 nums和一个整数 target。向数组中的每个整数前添加 '+''-',然后串联起所有整数,可以构造一个表达式:
例如, nums = [2, 1],可以在 2 之前添加 '+',在 1之前添加 '-',然后串联起来得到表达式 "+2-1"
返回可以通过上述方法构造的、运算结果等于 target的不同表达式的数目。

示例 1:
输入: nums = [1,1,1,1,1], target = 3
输出: 5
解释:一共有 5种方法让最终目标和为 3
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:
输入: nums = [1], target = 1
输出: 1

提示:
   1 <= nums.length <= 20
   0 <= nums[i] <= 1000
   0 <= sum(nums[i]) <= 1000
   -1000 <= target <= 1000

二、问题分析

  乍一看这道题跟动态规划没什么关系。但是仔细分析题目,“向数组中的每个整数前添加’+‘’-’,这里不就是取不取的问题吗?那就可以用动态规划0-1背包来解决问题。那要怎样确定dp数组?怎样确定递推公式?这里先确定背包的最大容量bagSize,这要看你对数学的敏感程度了。
  先记数组的元素和为sum,记在前面添加“-”的元素和为sub,那么前面添加“+”的元素和为sum-sub,那么 ( s u m − s u b ) − s u b = t a r g e t (sum-sub)-sub=target (sumsub)sub=target,则 s u b = ( s u m − t a r g e t ) / 2 sub=(sum-target)/2 sub=(sumtarget)/2。由于sum和target已经确定,那么sub也就是确定得,就可以确定背包最大容量为sub。从数组取出部分元素使得总和刚好为sub,而题目要求的是方法数,转化为装满容量为sub的背包有几种方法。
  注意:从题目提示中知道数组所元素是非负整数,那么sub必须也非负整数,即target<=sum,在提示中target可以是负数,当全部数组元素都取“-”时,target为最小,即target>=-sum,也就是abs(target)<=sum。还有就是sum-target必须能够整除2,即(sum - target) % 2 == 0

  • 确定dp数组及下标的含义:dp[ j j j]表示装满 j j j(包括 j j j)这么大容量的背包,有dp[ j j j]种方法。
  • 确定递推公式:在不考虑nums[ i i i]的情况下,装满容量为 j j j-nums[ i i i]的背包,有dp[ j j j-nums[ i i i]]种方法,那么只要找到nums[ i i i],凑成dp[ j j j]就有dp[ j j j-nums[ i i i]]种方法,所以递推公式为:dp[j] += dp[j-nums[i]]
  • 初始化dp数组:从递推公式看出,只要初始化dp[0]就可以了,而装满容量为0背包有1种方法,也就是装0件物品,即dp[0]=1,而其他数则初始为0就可以。
  • 确定遍历顺序:遍历顺序逻辑与0-1背包是一样的,使用一维数组,遍历物品的for循环放在外层,遍历背包的for循环放在内层,且内层for循环使用倒叙遍历。

三、代码实现

// 编程软件:VS2019
// 参考书籍:代码随想录

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

// 动态规划:时间复杂度O(n×(sum-target)),空间复杂度O(sum-target)
int findTargetSumWays(vector<int>& nums, int target) {
	int sum = 0;
	for (int i = 0; i < nums.size(); i++) sum += nums[i];
	if (abs(target) > sum) return  0; // target绝对值大于sum,就直接返回false
	if ((sum - target) % 2 != 0) return 0; // 不能整除2,就直接返回false
	int bagSize = (sum - target) / 2; // 最大背包容量
	vector<int> dp(bagSize + 1, 0);
	dp[0] = 1;
	// 0-1背包逻辑
	for (int i = 0; i < nums.size(); i++) {
		for (int j = bagSize; j >= nums[i]; j--) {
			dp[j] += dp[j - nums[i]];
		}
	}
	return dp[bagSize];
}

int main() {
	vector<int>nums = { 1,1,1,1,1 };
	int target = 3;
	cout << findTargetSumWays(nums, target);
}
// 结果:5

四、小结

  这道题首先要看得出与动态规划有关(这就要平时多做多看一些动态规划得题目了),然后再去推导出最大背包容量,再转化成装满容量sub容量方法有几种,而对于求装满背包有几种方法问题,递推公式一般都为 dp[j] += dp[j-nums[i]]
力扣:416. 分割等和子集
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeKwang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值