动态规划part4 | ● 416. 分割等和子集

文章目录

  • 416. 分割等和子集
    • 思路
    • 思路代码
    • 官方题解
    • 代码
    • 困难
  • 今日收获


416. 分割等和子集

416.分割等和子集

思路

背包问题
滚动数组优化
dp[j]=dp[j-nums[i]]||dp[j]
问题转换成容量为总数除以2的背包能不能放满,两种情况,当前的物品放了和当前的没放

这种方式直接将dp数组的值存储为能否放入,即bool值,显然更加优化。

思路代码

func canPartition(nums []int) bool {
    sum:=0
    for _,v:=range nums{
        sum+=v
    }
    if sum%2!=0{
        return false
    }
    sum=sum/2
    dp:=make([]bool,sum+1)
    dp[0]=true
    for i:=0;i<len(nums);i++{
        for j:=sum;j>=nums[i];j--{
            dp[j]=dp[j-nums[i]]||dp[j]
        }
    }
    return dp[sum]
}

官方题解

即一个商品如果可以重复多次放入是完全背包,而只能放入一次是01背包,写法还是不一样的。

要明确本题中我们要使用的是01背包,因为元素我们只能用一次。

回归主题:首先,本题要求集合里能否出现总和为 sum / 2 的子集。

那么来一一对应一下本题,看看背包问题如何来解决。

只有确定了如下四点,才能把01背包问题套到本题上来。

背包的体积为sum / 2
背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
背包如果正好装满,说明找到了总和为 sum / 2 的子集。
背包中每一个元素是不可重复放入。
以上分析完,我们就可以套用01背包,来解决这个问题了。

代码

// 分割等和子集 动态规划
// 时间复杂度O(n^2) 空间复杂度O(n)
func canPartition(nums []int) bool {
    sum := 0
    for _, num := range nums {
        sum += num
    }
    // 如果 nums 的总和为奇数则不可能平分成两个子集
    if sum % 2 == 1 {
        return false
    }
    
    target := sum / 2
    dp := make([]int, target + 1)

    for _, num := range nums {
        for j := target; j >= num; j-- {
            if dp[j] < dp[j - num] + num {
                dp[j] = dp[j - num] + num
            }
        }
    }
    return dp[target] == target
}

困难

问题转换成容量为总数除以2的背包能不能放满,两种情况,当前的物品放了和当前的没放


今日收获

0-1背包问题,思路是判断当前物品放不放入背包中,即0-1的状态。
同时能对2维dp优化,滚动数组模拟上一层dp,因为只需要上一层dp,为了不影响上一层dp的状态,即不将物品重复放入背包,因为是从左上方取值的,所以应该倒叙遍历。

你可能感兴趣的:(算法题,动态规划,算法,leetcode)