494. 目标和
解题思路:
这是一个dp求组合数问题。
将问题转换成背包问题,需要求出背包的容量。求数组内的目标和,可以转换成:left - right = target。
- left:代表数组内前面添加+的元素和
- right:代表数组内前面添加-的元素和
left + right = sum
left - (sum - left) = target
left = (sum + target)/2
有上述推导,我们可以将原问题转换成求解将背包容量为left的背包装满有多少种可能。
其中当sum + target为奇数时,是不可能产生这样的组合
一、dp数组的含义
dp[i][j]:表示前j个元素将容量为i的背包装满有dp[i][j]种情况
二、dp递推公式
当元素nums[ j ] > i时,说明该元素不可能被放进背包,因此dp[ i ][ j ] = dp[ i ][ j-1 ]
当当元素nums[ j ] <= i时,该元素有两种选择,一种是选择不放入,那么和上式相同,一种是选择放入,那么有dp[ i-nums[ j ] ][ j-1]种可能,因此总的推到关系是为:dp[ i ][ j ] = dp[ i-nums[ j ] ][ j-1] + dp[ i ][ j-1 ]
三、初始化
当背包容量为0,数组内元素数量也为0时,那么就只有一种可能的选择,因此dp[0][0]=1
四、代码实现
func findTargetSumWays(nums []int, target int) (count int) {
sum:=0
for i:=0;i<len(nums);i++{
sum+=nums[i]
}
if target > sum||(sum+target)%2==1 || -target > sum{
return 0
}
bagSize := (sum+target) / 2
dp := make([][]int,bagSize+1)
for i:=0;i<=bagSize;i++{
dp[i] = make([]int,len(nums)+1)
}
dp[0][0]=1
for i:=0;i<=bagSize;i++{ // 遍历背包
for j,num := range nums{ // 遍历元素
// fmt.Println(j)
dp[i][j+1] = dp[i][j]
if i>=num{
dp[i][j+1]+=dp[i-num][j]
}
}
}
return dp[bagSize][len(nums)]
}
1049. 最后一块石头的重量 II
题目链接:最后一块石头的重量 II
解题思路:
最关键的步骤是将石头重量平均分成两份,middle = sum / 2。然后取middle为背包大小,从石头堆中尽可能的选取石头装满背包,最后的结果就是:(sum - dp[middle]) - dp[middle]
原理证明:证明
dp数组的含义:在容量为j的背包中,最大重量为dp[j]
dp数组递推公式:dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
其实上述内容何完全等价于0/1背包问题。题目的关键点是如何转化为0/1背包问题,这也是leetcode动态规划题目的难点所在
代码实现:
func lastStoneWeightII(stones []int) int {
sum := 0
for i:=0;i<len(stones);i++{
sum+=stones[i]
}
midsum := sum / 2
dp:= make([]int, midsum+1)
dp[0] = 0
for i:=0;i<len(stones);i++{
for j:=midsum;j>=stones[i];j--{
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
}
}
return (sum - dp[midsum]) - dp[midsum]
}
func max(a,b int)int{
if a > b{
return a
}else{
return b
}
}
474.一和零
题目链接:474.一和零
解题思路:
这道题依然考察的是对于0/1背包的理解,核心问题就是将strs中包含最多m个0和n个1的最大子集转换成背包问题里的背包能装的最大价值。
首先是背包容量,根据题意,我们这里的背包容量就不是单纯的1个数字,而是背包内子集的0的数量和1的数量。因此这里背包的容量是一个二维的表示。物品的重量就相当于strs中一个元素的0和1的数量。由此分析,我们可以这样设定dp数组
一、dp数组的含义
dp[i][j]:表示在最多拥有i个0和j个1的子集中,拥有的最大长度为dp[i][j]
二、dp数组递推公式
问题转换成背包问题,那么dp数组的递推公式也是相似的
dp[i][j] = max(dp[i][j],dp[i-zeroNum][j-oneNum]+1)
代码实现:
func findMaxForm(strs []string, m int, n int) int {
dp := make([][]int, m+1)
for i:=0;i<m+1;i++{
dp[i] = make([]int,n+1)
}
for i := 0; i<len(strs);i++{
zeroNum := 0
oneNum := 0
for j:= 0;j<len(strs[i]);j++{
if strs[i][j] == '1'{
oneNum++
}else{
zeroNum++
}
}
for k:=m;k>=zeroNum;k--{
for t := n;t>=oneNum;t--{
dp[k][t] = max(dp[k][t],dp[k-zeroNum][t-oneNum]+1)
}
}
}
return dp[m][n]
}
func max(a,b int) int{
if a>b{
return a
}else{
return b
}
}