给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例 1:

输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释: 

-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

一共有5种方法让最终目标和为3。
注意:

数组的长度不会超过20,并且数组中的值全为正数。
初始的数组的和不会超过1000。
保证返回的最终结果为32位整数。

思路:

本题一看就是没有见过的动态规划  但是进行变化

//       可以化成两部分之差  target = sum(P) - sum(N);
//        那么sum(P) + sum(N) + sum(P) - sum(N) = sum(S) + target ;
//      2sum(P)= sum(S) + target ;
//        那么sum(P) = [target + sum(S)] / 2;

 

一维动态

// 改一维 对dp[i][j]而言 相当于每个都依赖上一行的相同位置 和 上一行左边位置 所以得从后往前遍历更新 保证不会先覆盖上一行左边的值
	public static int findTargetSumWays2(int[] nums, int S) {
		int sum = 0;
		for (int i : nums) {
			sum += i;
		}
		if (sum < S || (sum + S) % 2 != 0) {
			return 0;
		}
		int target = (S + sum) / 2;
		// 寻找和为target的种数
		int[] dp = new int[target + 1];

		dp[0] = 1;
		// 在每一个位置都可以选择是否要当前位置的数
		for (int i = 0; i < nums.length; i++) { // 从1位置开始
			for (int j = target; j >= nums[i]; j--) {
				// 要当前值nums[i] 和 不要当前值nums[i]
				dp[j] = dp[j] + dp[j - nums[i]];

			}
		}
		return dp[target];
	}