相似题目:
Leetcode416. 分割等和子集
题目:
给你一个整数数组 n u m s nums nums 和一个整数 t a r g e t target target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如, n u m s = [ 2 , 1 ] nums = [2, 1] nums=[2,1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 t a r g e t target 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
题解:
0-1背包问题
记数组的元素和为 sum \textit{sum} sum,添加 - \texttt{-} - 号的元素之和为 neg \textit{neg} neg,则其余添加 + \texttt{+} + 的元素之和为 sum − neg \textit{sum}-\textit{neg} sum−neg,得到的表达式的结果为
( sum − neg ) − neg = sum − 2 ⋅ neg = target ( s u m − n e g ) − n e g = s u m − 2 ⋅ n e g = t a r g e t (\textit{sum}-\textit{neg})-\textit{neg}=\textit{sum}-2\cdot\textit{neg}=\textit{target} (sum−neg)−neg=sum−2⋅neg=target (sum−neg)−neg=sum−2⋅neg=target(sum−neg)−neg=sum−2⋅neg=target
即
neg = sum − target 2 \textit{neg}=\dfrac{\textit{sum}-\textit{target}}{2} neg=2sum−target
由于数组 nums \textit{nums} nums 中的元素都是非负整数, neg \textit{neg} neg 也必须是非负整数,所以上式成立的前提是 sum − target \textit{sum}-\textit{target} sum−target是非负偶数。若不符合该条件可直接返回 0。
若上式成立,问题转化成在数组 nums \textit{nums} nums 中选取若干元素,使得这些元素之和等于 neg \textit{neg} neg,计算选取元素的方案数。我们可以使用动态规划的方法求解。
定义二维数组 dp \textit{dp} dp,其中 dp [ i ] [ j ] \textit{dp}[i][j] dp[i][j] 表示从数组的 [ 0 , i ] [ 0 , i ] [0,i]下标范围内选取若干个正整数,使得这些元素之和等于 j j j 的方案数。假设数组 nums \textit{nums} nums 的长度为 n,则最终答案为 dp [ n − 1 ] [ neg ] \textit{dp}[n-1][\textit{neg}] dp[n−1][neg]。
在定义状态之后,需要考虑边界情况。以下两种情况都属于边界情况。
当 1 ≤ i < n 1 \le i < n 1≤i<n时,对于数组 nums \textit{nums} nums中的第 i i i 个元素 num \textit{num} num(i 的计数从 1 开始),遍历 0 ≤ j ≤ neg 0 \le j \le \textit{neg} 0≤j≤neg,计算 dp [ i ] [ j ] \textit{dp}[i][j] dp[i][j] 的值:
如果 j < num j < \textit{num} j<num,则不能选 num \textit{num} num,此时有 dp [ i ] [ j ] = dp [ i − 1 ] [ j ] \textit{dp}[i][j] = \textit{dp}[i - 1][j] dp[i][j]=dp[i−1][j];
如果 j ≥ num j \ge \textit{num} j≥num,则如果不选 num \textit{num} num,方案数是 dp [ i − 1 ] [ j ] \textit{dp}[i - 1][j] dp[i−1][j],如果选 num \textit{num} num,方案数是 dp [ i − 1 ] [ j − num ] \textit{dp}[i - 1][j - \textit{num}] dp[i−1][j−num],此时有 dp [ i ] [ j ] = dp [ i − 1 ] [ j ] + dp [ i − 1 ] [ j − num ] \textit{dp}[i][j] = \textit{dp}[i - 1][j] + \textit{dp}[i - 1][j - \textit{num}] dp[i][j]=dp[i−1][j]+dp[i−1][j−num]。
因此状态转移方程如下:
dp [ i ] [ j ] = { dp [ i − 1 ] [ j ] , j < nums [ i ] dp [ i − 1 ] [ j ] + dp [ i − 1 ] [ j − nums [ i ] ] , j ≥ nums [ i ] \textit{dp}[i][j]=\begin{cases} \textit{dp}[i - 1][j], & j<\textit{nums}[i] \\ \textit{dp}[i - 1][j] + \textit{dp}[i - 1][j - \textit{nums}[i]], & j \ge \textit{nums}[i] \end{cases} dp[i][j]={dp[i−1][j],dp[i−1][j]+dp[i−1][j−nums[i]],j<nums[i]j≥nums[i]
最终得到 dp [ n − 1 ] [ neg ] \textit{dp}[n-1][\textit{neg}] dp[n−1][neg] 的值即为答案。
java代码:
/**
* @param nums
* @param target
* @return
*/
public static int findTargetSumWays(int[] nums, int target) {
int len = nums.length;
int sum = 0;
for (int i = 0; i < len; i++) {
sum += nums[i];
}
if ((sum - target) % 2 != 0) {
return 0;
}
int neg = (sum - target) / 2;
//dp[i][j] :在数组[0,i]中选取一些数,使得和为j
int[][] dp = new int[len][neg + 1];
//i=0时,只有一个正数nums[0]可以被选取
dp[0][nums[0]] = 1;
for (int i = 0; i < len; i++) {
//从[0,i]中不选任何正整数
dp[i][0] = 1;
}
for (int i = 1; i < len; i++) {
for (int j = 1; j <= neg; j++) {
if (j < nums[i]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
}
}
}
return dp[len - 1][neg];
}