背包问题详解与力扣上的背包问题(待更)

目录

1 0-1背包问题

1.1 问题分析

1.2 算法设计

1.3 完美图解

1.4 伪代码详解

1.5 实战演练

1.6 算法解析和优化拓展

重点注意

1.7 LeetCode上的0-1背包问题

分割等和子集(medium难度)(0-1背包问题,背包问题的基础)

一和零(medium难度)(0-1背包问题)

目标和(medium难度)(0-1背包问题)

2 完全背包问题详解

2.1 《算法笔记》关于完全背包问题的讲解

2.2 《背包九讲》关于完全背包问题的讲解

2.3 完全背包求最优解(不需要装满)的递推公式

2.4 完全背包解决排列组合问题(恰好装满)的递推公式

2.5 LeetCode上的完全背包问题

零钱兑换(medium难度)

零钱兑换Ⅱ(medium难度)(求方案数)

3 背包问题的初始化

3.1 求最优解的背包问题中:装满/不需要装满对应的初始化

3.2 求方案数的背包问题中:恰好装满(或装到指定容量)对应的初始化


1 0-1背包问题

1.1 问题分析

1.2 算法设计

1.3 完美图解

1.4 伪代码详解

1.5 实战演练

1.6 算法解析和优化拓展

注:上方图片来自陈小玉的《趣学算法》(P210-P220)

重点注意

算法定义部分:

 

算法优化部分:

1.7 LeetCode上的0-1背包问题

分割等和子集(medium难度)(0-1背包问题,背包问题的基础)

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

本题方法思路和代码来源:
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)

参考代码1:

public class Solution {

    public boolean canPartition(int[] nums) {
        int len = nums.length;
        // 题目已经说非空数组,可以不做非空判断
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        // 特判:如果是奇数,就不符合要求
        if ((sum & 1) == 1) {
            return false;
        }

        int target = sum / 2;
        // 创建二维状态数组,行:物品索引,列:容量(包括 0)
        boolean[][] dp = new boolean[len][target + 1];

        // 先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满
        if (nums[0] <= target) {
            dp[0][nums[0]] = true;
        }
        // 再填表格后面几行
        for (int i = 1; i < len; i++) {
            for (int j = 0; j <= target; j++) {
                // 直接从上一行先把结果抄下来,然后再修正
                dp[i][j] = dp[i - 1][j];
                
                //j恰好等于nums[i],即单独nums[j] 这个数恰好等于此时j(背包的容积)
                if (nums[i] == j) {
                    dp[i][j] = true;
                    continue;
                }
//不选择nums[i],如果在[0, i - 1]这个子区间内已经有一部分元素,使得它们的和为j ,那么dp[i][j] = true;
//选择nums[i],如果在[0, i - 1]这个子区间内就得找到一部分元素,使得它们的和为j - nums[i]。
                if (nums[i] < j) {
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }
        }
        return dp[len - 1][target];
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)

参考代码2:

public class Solution {

    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if ((sum & 1) == 1) {
            return false;
        }

        int target = sum / 2;
        boolean[][] dp = new boolean[len][target + 1];
        
        // 初始化成为 true 虽然不符合状态定义,但是从状态转移来说是完全可以的
        dp[0][0] = true;

        if (nums[0] <= target) {
            dp[0][nums[0]] = true;
        }
        for (int i = 1; i < len; i++) {
            for (int j = 0; j <= target; j++) {
                dp[i][j] = dp[i - 1][j];
                if (nums[i] <= j) {
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }

            // 由于状态转移方程的特殊性,提前结束,可以认为是剪枝操作
            if (dp[i][target]) {
                return true;
            }
        }
        return dp[len - 1][target];
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)

这里可能会有人困惑为什么压缩到一维时,要采用逆序。因为在一维情况下,是根据

dp[j] || dp[j - nums[i]]

来推d[j]的值,如不逆序,就无法保证在外循环 i 值保持不变 j 值递增的情况下,dp[j - num[i]]的值不会被当前所放入的nums[i]所修改&#

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值