问题描述
问题分析
- 一看是统计所有可能性个数问题,便知是用动态规划。
- 先从暴力递归入手,
int countWays(int[] nums, int i, int target)
返回 nums[i ~ end] 能否有几种方式可以加出target(基于减法的思想)。对于 nums[i]元素,有加和减两种决策,分别进行递归,两种决策可能性之和即为所求。
- 但改写动态规划时却发现,target 有可能取值为负数,而二维数组的索引不可能为负数,所以只能建立一个映射关系,如果对
nums
数组求和为sum
,那么 target + sum 一定是在[0, sum]之间(先保证了初始target绝对值不能超过sum),所以,将[-sum, sum] 映射为[0, 2*sum+1]的形式即可。具体见实现。
经验教训
- 暴力递归改写动态规划时,发现坐标有可能取负值怎么办?
代码实现
public int findTargetSumWays(int[] nums, int S) {
if(nums == null) {
return 0;
}
return countWays(nums, 0, S);
}
public int countWays(int[] nums, int i, int target) {
if (i == nums.length) {
if (target == 0) {
return 1;
}
return 0;
}
return countWays(nums, i + 1, target - nums[i]) + countWays(nums, i + 1, target + nums[i]);
}
public int findTargetSumWays(int[] nums, int S) {
if(nums == null) {
return 0;
}
int sum = 0;
for (int num : nums) {
sum += num;
}
if (sum < Math.abs(S)) {
return 0;
}
int[][] dp = new int[nums.length + 1][2 * sum + 1];
dp[nums.length][sum] = 1;
for (int i = nums.length - 1; i >= 0; --i) {
for (int j = 0; j < 2 * sum + 1; ++j) {
if (j - nums[i] >= 0) {
dp[i][j] += dp[i + 1][j - nums[i]];
}
if (j + nums[i] < 2 * sum + 1) {
dp[i][j] += dp[i + 1][j + nums[i]];
}
}
}
return dp[0][S + sum];
}