关键词:动态规划 01背包 dfs回溯
一个套路:
- 01背包:空间优化之后dp【target+1】,遍历的时候要逆序遍历
- 完全背包:空间优化之后dp【target+1】,遍历的时候要正序遍历
题目:
解法一:
dfs 回溯
思路:
数组nums 的每个元素都可以添加符号+或-,因此每个元素有⒉种添加符号的方法,n个数共有2^n种添加符号的方法,对应2^n种不同的表达式。当n个元素都添加符号之后,即得到─种表达式,如果表达式的结果等于目标数target,则该表达式即为符合要求的表达式。
可以使用回溯的方法遍历所有的表达式,回溯过程中维护一个计数器count,当遇到一种表达式的结果等于目标数target时,将count的值加1。遍历完所有的表达式之后,即可得到结果等于目标数target的表达式的数目。
因为nums最多只有20,所以暴力的dfs应该是不会爆的。
回顾一下之前的dfs笔记吧!
中止条件:step>nums.size()
count统计符合个数
分出两个dfs,一个给+一个给-
复杂度计算:
时间复杂度O(2^n)
空间复杂度O(n)
代码:
class Solution {
public:
int findTargetSumWays(std::vector<int>& nums, int target) {
int count = 0, sum = 0;
int step = 1;
dfs(nums, target, step, sum, count);
return count;
}
void dfs(std::vector<int>& nums, int target, int step, int sum, int& count)
{
if (step == nums.size() + 1)
{
if(sum == target)
count++;
return;
}
dfs(nums, target, step + 1, sum + nums[step - 1], count);
dfs(nums, target, step + 1, sum - nums[step - 1], count);
}
};
解法二:
动态规划 01背包
思路:
可以用非常巧的办法转换成用动态规划做。
得到新的的目标为neg。
之后用01背包的知识就可以完成。
状态:dp[j]:前i个元素中,凑到目标j的方法总数。
转移方程:dp[j]=dp[j]+dp[j-nums[i]]
- dp[j]:不需要第i个元素nums[i]的情况下,凑到目标j的方法总数。
- dp[j-nums[i]]:需要第i个元素nums[i]的情况下,凑到目标j的方法总数。
初始条件:因为是计算总和所以设置为0
边界:dp[0]=1 前0个元素,凑到目标0的方法总数为1
复杂度计算:
时间复杂度O(nm) n=neg m=nums.size()
空间复杂度O(n) n=neg
代码:
class Solution {
public:
int findTargetSumWays(std::vector<int>& nums, int target) {
int sum = 0;
for (const auto& x : nums)
sum += x;
int diff = sum - target;
if (diff < 0 || diff & 1)
return 0;
int tar = diff / 2;
std::vector<int> dp(tar + 1);
//边界条件:当没有任何元素可以选取时,元素和只能是 0,对应的方案数是 1
dp[0] = 1;//装0个重量,用0个装,一共有一种方法
for (int i = 0; i < nums.size(); ++i)
{
for (int j = tar; j >= nums[i]; --j)
{
dp[j] += dp[j - nums[i]];
}
}
return dp[tar];
}
};