今天是第38天刷leetcode,立个flag,打卡60天。
算法挑战链接
494. 目标和https://leetcode.cn/problems/target-sum/
第一想法
题目理解:题目给出一个数组,使用 + 或 - 算术符号,有多少种组合可以得到target的值。
拿到题目的第一想法是使用回溯算法,因为可以挨个去尝试。如果将所有的结果都尝试了,就可以得出有多少种组合可以满足。
但是很不幸,超时了。
看完代码随想录之后的想法
依旧是 0-1 背包问题的解决方案。
之前我做的0-1背包解决的问题是:容量为J的背包,价值最大的解决方案,和这个组合的好像是不相干的。的确也是,看完代码随想录之后,明白了,这个是0-1背包的变形。
跟着动态规划的五部曲走一遍,或许可以明白这个变形是怎么来的。
动态规划的五部曲走起:
- 确定dp数组(dp table)以及下标的含义
dp[j] 代表的含义是:在 0-i 物品任取的情况下,价值为 J 的组合有 dp[j] 种
- 确定递推公式
这个可以从开始递推。
假设数组是[0,1,2,3,4,5],target 5
取0的时候,有dp[5]种方式
取1的时候,有dp[4]种方式
取2的时候,有dp[3]种方式
取3的时候,有dp[2]种方式
取4的时候,有dp[1]种方式
取5的时候,有dp[0]种方式
如果不明白可以在想想 dp[j] 的含义。
那么dp[5] = dp[0] + dp[1] + dp[2] + dp[3] + dp[4] + dp[5]
那么抽象在代码中可以写成是:
dp[j] += dp[j - nums[i]]
- dp的初始化
dp[0] = 1,解释一下为什么dp[0] 需要初始化为1。
其实不要硬去解释它的含义,咱就把 dp[0]的情况带入本题看看应该等于多少。
如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。
所以本题我们应该初始化 dp[0] 为 1。
可能有同学想了,那 如果是 数组[0,0,0,0,0] target = 0 呢。
其实 此时最终的dp[0] = 32,也就是这五个零 子集的所有组合情况,但此dp[0]非彼dp[0],dp[0]能算出32,其基础是因为dp[0] = 1 累加起来的。
dp[j]其他下标对应的数值也应该初始化为0,从递推公式也可以看出,dp[j]要保证是0的初始值,才能正确的由dp[j - nums[i]]推导出来。
- 确定遍历顺序
先遍历物品,然后在遍历容量,遍历容量的时候需要从大到小开始遍历,因为有每个物品只能使用一次的限制。
- 举例推导dp数组
数据就不推导了。
今日收获
有些东西是会变化的,也许这个就是区分普通和厉害的一个很重要的点。