问题描述
问题分析
一看是统计所有可能性个数问题 ,便知是用动态规划 。 先从暴力递归入手,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 ];
}