-
1049最后一块石头的重量
- 代码随想录 (programmercarl.com)
-
第一印象
- 本题的逻辑可以转化为,将整个数组分为两个子集,让两个子集和的差最小,或者说使其中一个子集的和更接近总和的一半。可以利用类似416分割等和子集的方法,当背包容量为target/2时,就可以得到其中少的子集。
-
讲解观后感
- ·确定dp数组以及下标的含义
dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。
- ·确定递推公式
01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
- ·dp数组如何初始化
- 计算出石头总重量 然后除2,得到dp数组的大小。
- 接下来就是如何初始化dp[j]呢,因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。
- ·
- 确定遍历顺序
在动态规划:关于01背包问题,你该了解这些!(滚动数组) (opens new window)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
-
解题代码
-
func lastStoneWeightII(stones []int) int {
dp := make([]int, 15001)
sum := 0
for _, v := range stones {
sum += v
}
target := sum / 2
for i := 0; i < len(stones); i++ {
for j := target; j >= stones[i]; j-- {
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
}
}
return sum - 2 * dp[target]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
-
494目标和
- 代码随想录 (programmercarl.com)
-
第一印象
- 可以让所有的数字一开始即为负数,然后当改变其中一个数字的符号时,相当于在背包中放入价值二倍的物品,背包初始价值是负数的总和。我们要统计所有价值为S的方法。
-
讲解观后感
- 卡尔使用了不同的推理方式初始化背包容量,这种方法能够更好的统计方法数量。我采用不同的描述方式进行了整理。
- 假设正数的总和为x,那么负数对应的总和就是
-(sum - x)。(sum为所有数字正相加)
所以 x +(- (sum - x)) = target
- 所以
x = (target + sum) / 2
- 我们定义dp数组以及下标的含义。
dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
-
确定递推公式
- 例如:dp[j],j 为5,
- 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
- 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
- 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
- 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
- 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
- 那么凑整dp[5]有多少方法呢,也就是把 所有的
dp[j - nums[i]] 累加起来。
- 所以递推公式
-
dp[j] += dp[j - nums[i]]
- 初始化
dp[0]=1,内循环倒序
-
解题代码
-
func findTargetSumWays(nums []int, target int) int {
sum := 0
for _, v := range nums {
sum += v
}
if abs(target) > sum {
return 0
}
if (sum+target)%2 == 1 {
return 0
}
bag := (sum + target) / 2
dp := make([]int, bag+1)
dp[0] = 1
for i := 0; i < len(nums); i++ {
for j := bag; j >= nums[i]; j-- {
dp[j] += dp[j-nums[i]]
}
}
return dp[bag]
}
func abs(x int) int {
return int(math.Abs(float64(x)))
}
-
474一和零
- 代码随想录 (programmercarl.com)
-
讲解观后感
- 本题还是01背包的问题,只不过背包容量是由m和n两个维度构成的。而每个物品就是字符串数组中的字符串。
- 使用
dp[i][j]表示容量m=i、n=j时最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
- 但此时的递推公式其实是一维的,因为"物品"维度是字符串。
- 递推公式:
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
- 初始
dp[0][0]=0即可
- 那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
-
解题代码
-
func findMaxForm(strs []string, m int, n int) int {
dp := make([][]int, m+1)
for i,_ := range dp {
dp[i] = make([]int, n+1 )
}
for i:=0;i<len(strs);i++ {
zeroNum,oneNum := 0 , 0
for _,v := range strs[i] {
if v == '0' {
zeroNum++
}
}
oneNum = len(strs[i])-zeroNum
for j:= m ; j >= zeroNum;j-- {
for k:=n ; k >= oneNum;k-- {
dp[j][k] = max(dp[j][k],dp[j-zeroNum][k-oneNum]+1)
}
}
}
return dp[m][n]
}
func max(a,b int) int {
if a > b {
return a
}
return b
}