动态规划-----494.目标和
题目描述:
给你一个非负整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式 :
- 例如,
nums = [2, 1]
,可以在2
之前添加'+'
,在1
之前添加'-'
,然后串联起来得到表达式"+2-1"
。
返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 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
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
转换为动态规划问题
这道题⽬咋眼⼀看和动态规划背包啥的也没啥关系。
本题要如何使表达式结果为target
,
既然为target
,那么就⼀定有 left组合 - right组合 = target
。
left + right = sum
,⽽sum
是固定的。right = sum - left
公式来了, left - (sum - left) = target
推导出 left = (target + sum)/2
。
target
是固定的,sum
是固定的,left
就可以求出来。
所以就转为组合问题,采取动态规划:此时问题就是在集合nums
中找出和为left
的组合。
题解:
一维数组的动态规划
pd[j]
表示:j
容量的背包,有几种方法。
class Solution {
public int findTargetSumWays (int[] nums, int target){
int sum = Arrays.stream(nums).sum();
if ((sum + target) % 2 == 1) return 0;
if (Math.abs(target) > sum) return 0;
int[] dp = new int [(sum + target)/2 + 1];
//初始化
Arrays.fill(dp, 0);
dp[0] = 1;
//状态转换
for (int i = 0;i < nums.length; i++){ //表示依次加入nums[i]的元素
//正常的套路,从大到小
for (int j = (sum + target)/2;j >= nums[i];j ++){
//自己(不要这个元素,以前已经达到了所需背包的容量) + 要这个元素(所以要把这个元素的大小从容量中减去)
dp[j] = dp[j] + dp[j-nums[i]];
}
}
return dp[(sum + target)/2];
}
}
二维数组的动态规划
pd[i][j]
表示容量为j
,包含i
个元素的最多集中组合
public class Solution {
public int findTargetSumways (int[] nums, int target){
int sum = Arrays.stream(nums).sum();
if ((sum + target) % 2 == 1) return 0;
if (Math.abs(target) > sum) return 0;
int left = (sum + target)/2;
int[][] dp = new int [nums.length + 1][left + 1];
//初始化
for (int i = 0;i <= left;i ++){
dp[0][i] = 0;
}
for (int i = 0;i <= nums.length;i ++){
dp[i][0] = 1;
}
//状态转换
for (int i = 1; i <= nums.length;i++){
for (int j = 1; j <= left;j++){
if (j >= nums[i-1]){ //这里[i-1]是因为,第i个元素,在nums中是第i-1个
//这个nums[i]元素之前的状态,不要nums[i] + 要nums[i]
dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]];
}
else {
//只能从上一个状态变换过来,因为减去nums[i]之后为负数
dp[i][j] = dp[i-1][j];
}
}
}
return dp[nums.length][left];
}
}