题目意思很明确,最简单的是暴力法,对于每个元素nums[i],考虑+nums[i]和-nums[i]两种情况:
public class Solution {
int count = 0;
public int findTargetSumWays(int[] nums, int S) {
calculate(nums, 0, 0, S);
return count;
}
public void calculate(int[] nums, int i, int sum, int S) {
if (i == nums.length) {
if (sum == S)
count++;
} else {
calculate(nums, i + 1, sum + nums[i], S);
calculate(nums, i + 1, sum - nums[i], S);
}
}
}
此外,可以用动态规划:
对于每一种可能的情况,总由一些正数项和负数项相加而成,记整数项之和为S(P),负数项之和为Sum(N) ,则Sum(P)-Sum(N)=target,此外,记数组nums元素之和为Sum,则Sum(P)+Sum(N)=Sum,则有Sum(P)=(Sum+target)/2。所以,此问题可以转化为求数组nums中,能够被划分成和为(Sum+target)/2的子集的个数。(集合划分问题)
public class Solution {
public int findTargetSumWays(int[] nums, int S) {
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
if (S > sum || (sum + S) % 2 == 1)
return 0;
return subsetSum(nums, (sum + S) / 2);
}
private int subsetSum(int[] nums, int S) {
int[] dp = new int[S + 1];
dp[0] = 1;//C(0,0)=1
for (int i = 0; i < nums.length; i++) {
for (int j = S; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[S];
}
}
还有另一种动态规划的思路:定义dp[i][j]为前i个元素组成sum为j的方法次数,则:
dp[i][j+nums[i]]+=dp[i-1][j],dp[i][j-nums[i]]+=dp[i-1][j]。
class Solution {
public int findTargetSumWays(int[] nums, int S) {
// backtrack
// return backtrack(nums, 0, S, 0);
if (nums == null || S > 1000 || S < -1000) {
return 0;
}
int sum = 0;
for (int n : nums) {
sum += n;
}
int[][] dp = new int[nums.length][sum * 2 + 1]; // [-sum, sum]
dp[0][nums[0] + sum] = 1;
dp[0][-nums[0] + sum] += 1;
for (int i = 1; i < nums.length; i++) {
for (int n = -sum; n <= sum; n++) {
if (dp[i - 1][n + sum] > 0) {
dp[i][n + sum + nums[i]] += dp[i - 1][n + sum];
dp[i][n + sum - nums[i]] += dp[i - 1][n + sum];
}
}
}
return S > sum ? 0 : dp[nums.length - 1][S + sum];
}
// private int backtrack(int[] nums, int index, int S, int curr) {
// if (index == nums.length) {
// return S == curr ? 1 : 0;
// }
// return backtrack(nums, index + 1, S, curr + nums[index]) + backtrack(nums, index + 1, S, curr - nums[index]);
// }
}