力扣-动态规划-494. 目标和
494. 目标和
题目描述
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:动态规划 01背包
昨天写博客总感觉缺少了点什么,今天必须把博客写上。
今天接着刷01背包的题目,讲真,这道题卡了我很久,最后花了一个下午的时间总算弄出来了。然后对比答案,发现答案的思路更妙。我自己的思路我觉得更容易想到,但存在初始化比较复杂的问题,在初始化的时候得考虑周全才能通过所有的测试用例。
根据问题很容易想到每个元素有两种状态,加上绝对值或者减去绝对值。首先遍历每个元素求得绝对值总和为sum,因此dp(i,j)表示从下标为0到i的元素中组成的不同表达式的种类的个数,其中j在逻辑上表示绝对值总和,在实际代码中会转化为列的下标。
我自己写的代码如下,代码已经加了注释,各位小伙伴如果有什么问题可以在评论里提出来,欢迎大家交流。
//自己的思路 将问题转化为01背包的类似问题-构建一个二维矩阵
//元素的值有两种选择 +元素值 -元素值,最后得到的总和target相当于背包容量
//dp[i][j]相当于从0-i的元素下表中选择总和为j的表达式的数量
//dp[i][j] = dp[i - 1][j - value[j]] + dp[i - 1][j + value[j]];
//外层是元素,内层是元素总和 从上到下逐层遍历
//举例推导 ok
//初始化较为复杂 判断是否等于sum 判断是否是0
public int findTargetSumWays(int[] nums, int target) {
//首先统计所有元素的绝对值之和,得到元素和的最大和最小范围
int sum = 0;
int zeroNum = 0;
for (int ele : nums) {
sum += Math.abs(ele);
}
if (target < -sum || target > sum) {//target不在元素能得到的和的范围之内
return 0;
}
int length = sum + sum + 1;//列的数量
int[][] dp = new int[nums.length][length];
//初始化第一列
int tempSum = 0;
int tempZero = 0;//统计前面0的数量
for (int i = 0; i < nums.length; i++) {
tempSum += Math.abs(nums[i]);
if (nums[i] == 0) {
tempZero++;
}
if (tempSum == sum || tempSum == -sum) {
dp[i][0] = (int)Math.pow(2, tempZero);
}
}
dp[0][length - 1] = dp[0][0];
//初始化第一行
if (nums[0] == 0) {
dp[0][nums[0] + sum] = 2;
} else {
dp[0][nums[0] + sum] = 1;
dp[0][sum - nums[0]] = 1;
}
for (int i = 1; i < nums.length; i++) {
for (int j = 1 - sum; j <= sum; j++) {
int a = j + sum + nums[i];
int b = j + sum - nums[i];
int add = 0;//代表加的方式
int sub = 0;//代表减的方式
if (a < 0 || a >= length) {
add = 0;
} else {
add = dp[i-1][j + sum + nums[i]];
}
if (b < 0 || b >= length) {
sub = 0;
} else {
sub = dp[i-1][j + sum - nums[i]];
}
dp[i][j + sum] = add + sub;
}
}
return dp[nums.length - 1][target + sum];
}