1.状态表示
题目来源
494.目标和——力扣
测试用例
2.算法原理
1.状态表示
首先我们需要将问题简化,这里需要找到能将数组组合计算成为指定数字target的添加方式,那么我们就可以将数字分为两类,一类是前面添加"+"的,另一类则是添加"-"的,那么对这两类数字进行关系分析最终如下图得到了关于正数总和的表达式,因此就转化为将数组中的数字挑选出和恰好为a的组合方法有几种,也就是背包问题
接下来就可以表示每个位置的状态,上面寻找恰好等于某个指定的数字,那么就创建一个二维的dp表来存储选取数字的范围以及当前已选数字之和,即
dp[i][j]:在[0,i]的区间选取的数字之和完全等于j的选取方式总数
2.状态转移方程
首先依旧是背包问题的思路,对最后一个位置进行分类讨论,首先判断当第i个位置不会选取,此时就找到dp[i-1][j],判断此时的方法数;然后判断选取第i个位置的数,此时就需要寻找到dp[i-1][j-nums[i-1]]这个位置的dp表的值,然后加到总方法数中去,当然需要判断j>=nums[i-1]
3.初始化
首先dp[0][0]一定是一种方法,也就是在不选择任何数字此时的总和肯定为0那么就将dp[0][0]置为1,之后第一行其他位置代表不选数字但是此时的总和为j(j>=1),这种情况一定是不存在的,那么就置为0即可,然后对第一列来判断,此时第一列由于原数组中的数字可能为0,那么第一列的初始化就不确定,但是由于在后续中的判断条件一定是j>=nums[i-1],这就保证了在使用第一列时一定不会出现越界的情况,那么不妨不初始化第一列,直接在循环中填写即可
4.填表顺序
从上到下,每一行从左到右
5.返回值
返回dp表最后一个位置的值即可
3.实战代码
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target)
{
int m = nums.size();
int sum = 0;
for(auto e : nums)
{
sum += e;
}
int n = (sum + target) / 2;
if(n < 0 || (sum + target) % 2 == 1)
{
return 0;
}
vector<vector<int>> dp(m+1,vector<int>(n+1));
dp[0][0] = 1;
for(int i = 1;i <= m;i++)
{
for(int j = 0;j <= n;j++)
{
dp[i][j] += dp[i-1][j];
if(j >= nums[i-1])
{
dp[i][j] += dp[i-1][j-nums[i-1]];
}
}
}
return dp[m][n];
}
};
代码解析
优化代码(空间优化)