代码随想录day43|动态规划part05

494. 目标和

题目链接: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
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值