力扣: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
    评论
以下是力扣LeetCode)上的经典问题:两数之和(Two Sum)的 C 语言解法。 ```c #include <stdio.h> #include <stdlib.h> int* twoSum(int* nums, int numsSize, int target, int* returnSize) { int i, j; int* result = (int*)malloc(sizeof(int) * 2); *returnSize = 2; for (i = 0; i < numsSize - 1; i++) { for (j = i + 1; j < numsSize; j++) { if (nums[i] + nums[j] == target) { result[0] = i; result[1] = j; return result; } } } return NULL; } int main() { int nums[] = {2, 7, 11, 15}; int target = 9; int returnSize; int* result = twoSum(nums, sizeof(nums) / sizeof(nums[0]), target, &returnSize); if (result != NULL) { printf("[%d, %d]\n", result[0], result[1]); free(result); } return 0; } ``` 这个程序首先定义了一个函数 `twoSum()`,它接受一个整数数组 `nums`,数组的长度 `numsSize`,目标值 `target` 和一个指向整数变量的指针 `returnSize`。这个函数的目的是在给定数组中找到两个数,它们的和等于目标值,并返回这两个数的下标。 函数内部使用了两个嵌套的循环来枚举数组中的所有可能的组合,并检查它们的和是否等于目标值。如果找到了符合条件的组合,就把它们的下标存储在一个动态分配的整数数组中,并返回这个数组的指针。如果没有找到符合条件的组合,就返回空指针。 在 `main()` 函数中,我们定义了一个测试用例,即整数数组 `{2, 7, 11, 15}` 和目标值 `9`。然后我们调用 `twoSum()` 函数,传入这个测试用例,并打印出返回的结果。 值得注意的是,我们在 `twoSum()` 函数内部动态分配了一个整数数组 `result`,然后在 `main()` 函数中使用 `free()` 函数释放了它的内存,以避免内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CodeKwang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值