目标和问题
给定一个数组nums[],给定目标值target,对于数组中的n个数据,可以在这些数前加“+”或者“-”,最终组成target的方式有多少种?
解题思路
假设在这些值前面加“+”的数的总和为x,那么在这些值前面加“-”的数的总和为sum-x,那么就有公式:
- x-(sum-x) = target -> 2x-sum = target -> x = (target + sum) / 2
首先考虑几种极端情况:
- 1、当sum的绝对值之和小于target的绝对值时,返回0;
- 2、当x不是整数,也就是(target + sum)/2的结果不是整数的时候,返回0;
之后本题就转换成了0-1背包问题,求数组中的数组成x的方式有多少种。使用动态规划的方式解决:
- 1、定义dp数组,dp[j]表示组成j的组合方式有dp[j]种;
- 2、确定递推公式:
得到nums[i]之后,凑成dp[j]就有dp[j-nums[i]]种方法。 - 3、dp数组初始化:dp[0]应该为1,因为给数组中的元素前加“+”还是“-”,都是1种方法、
- 4、确定遍历顺序:和0-1背包中类似,nums放在外循环,target放在内循环,并且内循环倒置。
- 5、举例推导dp数组
代码如下
class Solution {
public int findTargetSumWays(int[] nums, int target) {
// 求和
int sum = 0;
for (int num :nums) {
sum += num;
}
if (target > 0 && target > sum) return 0;
if (target < 0 && target < -sum) return 0;
if ((target + sum) % 2 != 0) return 0;
int x = (target + sum) / 2; // 加法的总和
if (x < 0) x = -x;
int[] dp = new int[x + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++) {
for (int j = x; j >= nums[i] ; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[x];
}
}