【代码随想录二刷】Day42-动态规划-Go

代码随想录二刷Day42

今日任务

01 背包问题,你该了解这些!
01 背包问题,你该了解这些!滚动数组
416.分割等和子集
语言:Go

01背包理论基础

  1. 01背包:有n件物品和一个最多能背重量为w的背包,第i件物品的重量是weight[i],得到的价值是value[i],每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大
  2. 动规五部曲
    ① 确定dp数组及下标含义:dp[i][j]表示从下标为[0-i]的物品任取,放入容量为j的背包中,价值总和最大是dp[i][j]
    ② 确定dp数组递归公式:分为两种情况,放第i个物品和不放第i个物品;
    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]
    ③ 初始化dp数组:
    dp[i][0]=0
    if i > value[0] dp[0][j]=value[0]
    ④ 确定dp数组遍历顺序:先遍历物品还是背包是个难点
    ⑤ 举例推导dp数组:验证
  3. 滚动数组
    即只使用一维数组代替二维数组
    ① 确定dp数组及下标含义:dp[j]表示容量为j的背包中物品价值最大可以为dp[j]
    ② 确定dp数组递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]
    ③ 初始化dp数组:全部初始化为0即可
    ④ 确定dp数组遍历顺序:
    必须倒序遍历:保证每个物品只被放入一次
    必须外层遍历物品内层遍历背包:否则相当于背包内只放入了一个物品
    为什么二维数组没有这么多限制:数组每层的更新都要利用之前行/列的值,二维数组没有数据覆盖的问题,而一维数组的数据会被覆盖,所以要加以限制
    ⑤ 举例推导dp数组:验证

416. 分割等和子集

链接:https://leetcode.cn/problems/partition-equal-subset-sum/

func canPartition(nums []int) bool {
    sum := 0
    for i := 0; i < len(nums); i++ {
        sum += nums[i]
    }
    if sum % 2 == 1 {
        return false
    }
    target := sum / 2
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    //1.确定dp数组及下标含义:dp[i][j]表示从下标0-i的数据中取数据放入和为j的背包中的取法有dp[i][j]
    //2.确定dp数组递归公式:分三种情况
    //3.初始化dp数组:dp[i][0]=0,dp[0][nums[0]]=1
    //4.确定dp数组遍历顺序:外层物品,内层背包
    //5.举例推导dp数组
    dp := make([][]int, len(nums))
    for i := 0; i < len(nums); i++ {
        dp[i] = make([]int, target + 1)
    }
    if target < nums[0] {
        return false
    }
    dp[0][nums[0]] = 1
    for i := 0; i < len(nums); i++ {
        dp[i][0] = 0
    }
    for i := 1; i < len(nums); i++ {
        for j := 1; j <= target; j++ {
            if nums[i] == j {
                dp[i][j] = dp[i - 1][j] + 1
            } else if nums[i] < j {
                dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j]
            } else if nums[i] > j {
                dp[i][j] = dp[i - 1][j]
            }
        }
        if dp[i][target] > 0 {
            return true
        }
    }
    return false
}

/*

        0   1   2   3   4
    1   0   1   0   0   0
    2   0   1   1   1   0
    5   0   1   1   1   0

        0   1   2   3   4   5   6   7   8   9   10  11
    1   0   1   0   0   0   0   0   0   0   0   0   0
    5   0   1   0   0   0   1   1   0   0   0   0   0
    5   0   1   0   0   0   2   2   0   0   0   1   1
    11  0

*/

滚动数组改进

func canPartition(nums []int) bool {
    sum := 0
    for i := 0; i < len(nums); i++ {
        sum += nums[i]
    }
    if sum % 2 == 1 {
        return false
    }
    target := sum / 2
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    //1.确定dp数组及下标含义:dp[j]表示取出和为j的数据有dp[j]种可能
    //2.确定dp数组递归公式:
    //3.初始化dp数组:dp[i][0]=0,dp[0][nums[0]]=1
    //4.确定dp数组遍历顺序:外层物品,内层背包
    //5.举例推导dp数组
    if nums[0] > target {
        return false
    } else if nums[0] == target {
        return true
    }
    dp := make([]int, target + 1)
    dp[nums[0]] = 1
    for i := 1; i < len(nums); i++ {
        for j := target; j >= nums[i]; j-- {
            if j == nums[i] {
                dp[j] = dp[j] + 1
            } else {
                dp[j] = dp[j] + dp[j - nums[i]]
            }
        }
        if dp[target] > 0 {
            return true
        }
    }
    return false
}

/*

        0   1   2   3   4
    1   0   1   0   0   0
    2   0   1   1   1   0
    5   0   1   1   1   0

        0   1   2   3   4   5   6   7   8   9   10  11
    1   0   1   0   0   0   0   0   0   0   0   0   0
    5   0   1   0   0   0   1   1   0   0   0   0   0
    5   0   1   0   0   0   2   2   0   0   0   1   1
    11  0

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值