文章目录

  • 系列文章目录
  • 前言
  • 题录
    • 440 · 背包问题 III
    • 518. 零钱兑换 II
    • 377. 组合总和 Ⅳ
    • 70. 爬楼梯(进阶)
    • 322. 零钱兑换
    • 279. 完全平方数
    • 139. 单词拆分
  • 总结
    • 01 背包与完全背包区别
    • 背包问题的变形

系列文章目录

一、 数组类型解题方法一:二分法
二、数组类型解题方法二:双指针法
三、数组类型解题方法三:滑动窗口
四、数组类型解题方法四:模拟
五、链表篇之链表的基础操作和经典题目
六、哈希表篇之经典题目
七、字符串篇之经典题目
八、字符串篇之 KMP
九、解题方法:双指针
十、栈与队列篇之经典题目
十 一、栈与队列篇之 top-K 问题
十 二、二叉树篇之二叉树的前中后序遍历
十 三、二叉树篇之二叉树的层序遍历及相关题目
十 四、二叉树篇之二叉树的属性相关题目
十 五、 二叉树篇之二叉树的修改与构造
十 六、 二叉树篇之二叉搜索树的属性
十 七、二叉树篇之公共祖先问题
十 八、二叉树篇之二叉搜索树的修改与构造
十 九、回溯算法篇之组合问题
二 十、回溯算法篇之分割、子集、全排列问题
二十一、贪心算法篇之入门题目
二十二、贪心算法篇之进阶题目
二十三、动态规划篇之基础题目
二十四、动态规划篇之背包问题:01背包
更新中 …


前言

刷题路线来自 :代码随想录
一个商品只能放入一次是01背包,而完全背包每个商品的数量没有数量限制,也就是可以放入背包多次
如:

dp 数组:

题录

440 · 背包问题 III

Lintcode 链接(Leetcode 上没有原题)
给定 n 种物品, 每种物品都有无限个. 第 i 个物品的体积为 A[i], 价值为 V[i].再给定一个容量为 m 的背包. 问可以装入背包的最大价值是多少?

题解:
因为每个物品可以多次放入,所以在放的下的情况下,若要放下新物品的最大价值为 dp[i][j - A[i - 1]] + V[i - 1]),不同与 01 背包,这里是 dp[i][] 而不是 dp[i - 1][],表示在放下新物品的本层dp 数组中寻找放下新物品后剩下的空间的最大价值。

public class Solution {public int backPackIII(int[] A, int[] V, int m) {// write your code hereint row = V.length;int[][] dp = new int[row + 1][m + 1];for (int i = 1; i <= row; i++) {for (int j = 1; j <= m; j++) {if (j >= A[i - 1]) {   // 放得下,注意这里为 ≥dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - A[i - 1]] + V[i - 1]);} else {// 放不下dp[i][j] = dp[i - 1][j];}}}return dp[row][m];}
}

优化(一维滚动 dp 数组):
不同于 01 背包,这里遍历背包时是从前向后遍历,因为以一个物品可以放入多次

public class Solution {public int backPackIII(int[] A, int[] V, int m) {// write your code hereint row = V.length;int[] dp = new int[m + 1];for (int i = 0; i < row; i++) {  // 遍历物品for (int j = A[i]; j <= m; j++) {   // 遍历背包,从A[i],开始因为前边放不下,数组中的最大价值为不放入的值,不用改变dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]);}}return dp[m];}
}

518. 零钱兑换 II

Leetcode 链接

题解:
组合问题
第二层的循环遍历背包为从前向后遍历,下标从 coins[i] 开始,因为 coins[i] 之前放不下新物品,数组值不改变。

class Solution {public int change(int amount, int[] coins) {// 最大组合int row = coins.length;int[] dp = new int[amount + 1];dp[0] = 1;for (int i = 0; i < row; i++) {// 从前向后遍历for (int j = coins[i]; j <= amount; j++) {dp[j] = dp[j] + dp[j - coins[i]];}}return dp[amount];}
}

377. 组合总和 Ⅳ

Leetcode 链接

题解:
排列问题,不同于前边的组合问题不强调顺序。(1,5)和(5,1)是同一个组合,但是是两个不同的排列。这里需要满足以下两个条件

  1. 先遍历背包,后遍历物品
  2. 不能使用二维 dp 数组
class Solution {public int combinationSum4(int[] nums, int target) {int row = nums.length;int[] dp = new int[target + 1];dp[0] = 1;for (int j = 1; j <= target; j++) {for (int i = 0; i < row; i++) {if (j >= nums[i]) {dp[j] = dp[j] + dp[j - nums[i]];}}}return dp[target];}
}

70. 爬楼梯(进阶)

Leetcode 链接
将题中的可以爬 1 或 2 个台阶改为可以爬 1 或 2 或 … 或 m 个台阶。(力扣无原题)

题解:
排列问题

class Solution {public int climbStairs(int n) {int[] dp = new int[n + 1];dp[0] = 1;int m = 2; // m 表示每次可趴 1 - m 阶,这里为 1、2for (int j = 1; j <= n; j++) {   // 先遍历背包for (int i = 1; i <= m; i++) {   // 再遍历物品if (j >= i) {dp[j] = dp[j] + dp[j - i];}}}return dp[n];}
}

322. 零钱兑换

Leetcode 链接

题解:
背包大小为 amount,求最放满背包物品的最少数量

递推公式:Math.min(放入前最少硬币数,放入后最少硬币数)
放入后最少硬币数 = 1 + 放入后背包剩余空间能放入的最少硬币数
初始化: 除了0下标位置外,全部初始化为Integer的最大值max,保证第一个硬币的遍历正常
难点: 怎么判断有满足背包大小的硬币组合吗? 如:[2, 5] amount = 3,返回 -1

  1. 如果背包大小不够放下第一个物品,dp数组当前位置值不改变,就为max。
  2. 如果背包能装下新物品,但是剩下的空间没有组合满足,让当前位置值不变,原来为如果max现在就还是max
  3. 所以max就表示没有满足背包大小的组合
class Solution {public int coinChange(int[] coins, int amount) {int row = coins.length;int max = Integer.MAX_VALUE;int[][] dp = new int[row + 1][amount + 1];// dp[0][i] 中除dp[0][0] 为 0外,全初始化为 max // 在背包刚好装下新物品时,1 + dp[i][0] = 1for (int i = 1; i <= amount; i++) {dp[0][i] = max;}for (int i = 1; i <= row; i++) {for (int j = 1; j <= amount; j++) {if (j >= coins[i - 1] && (dp[i][j - coins[i - 1]] != max)) {// 如果背包能放下新物品,并且背包剩余大小有满足的组合dp[i][j] = Math.min(dp[i - 1][j], 1 + dp[i][j - coins[i - 1]]);} else {// 放不下或者 dp[i][j - coins[i - 1]] = max,表示无满足剩余背包大小的组合dp[i][j] = dp[i - 1][j];}}}return dp[row][amount] == max ? -1 : dp[row][amount];}
}

空间优化:

class Solution {public int coinChange(int[] coins, int amount) {int row = coins.length;int max = Integer.MAX_VALUE;int[] dp = new int[amount + 1];// 除dp[0] 外 都为max,dp[0] = 0for (int i = 1; i <= amount; i++) {dp[i] = max;}for (int i = 0; i < row; i++) {for (int j = coins[i]; j <= amount; j++) {if ((dp[j - coins[i]] != max)) {// 放下后,有满足背包剩余大小的组合dp[j] = Math.min(dp[j], 1 + dp[j - coins[i]]);} }}return dp[amount] == max ? -1 : dp[amount];}
}

279. 完全平方数

Leetcode 链接

题解:
同上题,这里的物品不同。背包大小为 n, 用数量不限的完全平方数大小的物品装满

class Solution {public int numSquares(int n) {int[] dp = new int[n + 1];int max = Integer.MAX_VALUE;for (int i = 1; i <= n; i++) {dp[i] = max;}for (int i = 1; i * i <= n; i ++) {  for (int j = i * i; j <= n; j++) {// 因为物品大小是从最小的 1 开始,所以不存在没有满足背包大小的组合情况, N 个1可以放满任何大小的背包,这里的if 语句可以省略//if (dp[j - i * i] != max) dp[j] = Math.min(dp[j], dp[j - i * i] + 1);}}return dp[n];}
}

139. 单词拆分

Leetcode 链接

题解:
dp[i]:[0, i] 的子串被分割后能在字典中找到
递推公式:dp[j] = dp[i] && 子串 [i , j] 能在字典中找到

class Solution {public static boolean wordBreak(String s, List<String> wordDict) {//Set<String> set = new HashSet<>(wordDict);int len = s.length();boolean[] dp = new boolean[len + 1];dp[0] = true;for (int j = 1; j <= len; j++) {for (int i = 0; i < j; i++) {dp[j] = dp[i] && wordDict.contains(s.substring(i, j));if (dp[j]) break;}}return dp[len];}
}

总结

  1. 统一 i 为物品下标,j 为背包下标。
  2. 当新增物品刚好放入背包时怎么做,决定怎么初始化 dp 数组。

01 背包与完全背包区别

  1. 01 背包中物品只能使用一次,完全背包物品可以使用无数次。
  2. 递推公式区别: 01背包在新物品放入有剩余的时候,剩余空间怎么放物品是不考虑放入过的新物品的,而完全背包可以再次放入新物品。所以二维递推公式前者背包剩余空间怎么放物品需要在没人放入前dp[i - 1][剩余V] 找,而完全背包就可以在 放入后的dp[i][剩余V] 找。而一维滚动数组, 01 背包是从后往前遍历,这样原来的状态得到最大程度的保留,而完全背包为从前往后遍历。
  3. 注意使用二维dp时,i = 0 留给初始化,保证 dp[i - 1][] 不会越界,所以物品数组 nums[i - 1] 为新添加的物品,而一维滚动dp数组时,物品从 0 下标开始,两者遍历的初始下标不同
  4. 使用一维dp数组时可以修改遍历的起始下标,代替二维 dp 数组中的内否放入背包的判断。

背包问题的变形

  1. 物品的最大价值(和)
    能放入时最大价值在放入新物品和 放入新物品产生的两种结果中取最大值
    01 背包:
    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + V[i - 1])
    dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]) (遍历背包时从后向前遍历)
    完全背包:
    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - A[i - 1]] + V[i - 1])
    dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]) (遍历背包时从前向后遍历)

  2. 背包中物品的组合
    能放入时组合数等于放入新物品组合数 放入新物品组合数
    01 背包:
    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
    dp[j] = dp[j] + dp[j - nums[i]]; (遍历背包时从后向前遍历)
    完全背包:
    dp[i][j] = dp[i - 1][j] + dp[i][j - nums[i - 1]];
    dp[j] = dp[j] + dp[j - nums[i]]; (遍历背包时从前向后遍历)

  3. 背包中物品的排列
    能放入时组合数等于放入新物品排列数 放入新物品排列数
    完全背包:
    dp[j] = dp[j] + dp[j - nums[i]](不可使用二维 dp 数组,必须先遍历背包,再遍历物品)

  4. 放满背包的最少物品
    初始化时 dp[0][0] = dp[0] = 1,dp[0][1~i] = dp[1 ~i] = max
    完全背包:
    if (dp[j - i * i] != max),有能满足的组合
    dp[i][j] = Math.min(dp[i - 1][j], 1 + dp[i][j - coins[i - 1]]);
    dp[j] = Math.min(dp[j], 1 + dp[j - coins[i]]);

Leetcode 刷题笔记(二十五) ——动态规划篇之背包问题:完全背包相关推荐

  1. Leetcode刷题笔记:栈与队列篇

    基础知识 栈和队列的原理大家应该很熟悉了,队列是先进先出,栈是先进后出. 如图所示: 那么我这里再列出四个关于栈的问题,大家可以思考一下.以下是以C++为例,使用其他编程语言的同学也对应思考一下,自己 ...

  2. 【leetcode刷题笔记】动态规划

    #[leetcode刷题笔记]动态规划 石子游戏 public boolean stoneGame(int[] piles) {int N = piles.length;// dp[i][j] is ...

  3. LeetCode刷题笔记-动态规划-day4

    文章目录 LeetCode刷题笔记-动态规划-day4 55. 跳跃游戏 1.题目 2.解题思路 3.代码 45. 跳跃游戏 II 1.题目 2.解题思路 3.代码 LeetCode刷题笔记-动态规划 ...

  4. 刷题笔记(十四)--二叉树:层序遍历和DFS,BFS

    目录 系列文章目录 前言 题录 102. 二叉树的层序遍历 BFS DFS_前序遍历 107. 二叉树的层序遍历 II BFS DFS 199. 二叉树的右视图 BFS DFS 637. 二叉树的层平 ...

  5. LeetCode刷题笔记(算法思想 四)

    LeetCode刷题笔记(算法思想 四) 七.动态规划 斐波那契数列 70. 爬楼梯 198. 打家劫舍 213. 打家劫舍 II 信件错排 母牛生产 矩阵路径 64. 最小路径和 62. 不同路径 ...

  6. LeetCode刷题笔记2——数组2

    LeetCode刷题笔记2--数组2 重塑数组 题目 在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原 ...

  7. LeetCode刷题笔记汇总

    LeetCode刷题笔记汇总 第一次刷LeetCode写的一些笔记. 1.两数之和 3.无重复字符的最长子串 15.三数之和 18.四数之和 19.删除链表的倒数第 N 个结点 20.有效的括号 21 ...

  8. leetcode刷题笔记——二分查找

    leetcode刷题笔记--二分查找 目前完成的贪心相关的leetcode算法题序号: 中等:80,81 困难:4 来源:力扣(LeetCode) 链接:https://leetcode-cn.com ...

  9. 卷进大厂系列之LeetCode刷题笔记:二分查找(简单)

    LeetCode刷题笔记:二分查找(简单) 学算法,刷力扣,加油卷,进大厂! 题目描述 涉及算法 题目解答 学算法,刷力扣,加油卷,进大厂! 题目描述 力扣题目链接 给定一个 n 个元素有序的(升序) ...

  10. 小何同学的leetcode刷题笔记 基础篇(01)整数反转

    小何同学的leetcode刷题笔记 基础篇(01)整数反转[07] *** [01]数学取余法*** 对数字进行数位操作时,常见的方法便是用取余的方法提取出各位数字,再进行操作 操作(1):对10取余 ...

最新文章

  1. 《剑指offer》跳台阶
  2. path manipulation怎么解决_干货!终于!解决macOS下pyenv安装python3.8.2缺少tkinter模块的问题!...
  3. 大数据之-Hadoop3.x_MapReduce_序列化案例FlowBean---大数据之hadoop3.x工作笔记0097
  4. oracle 数据库-数据字典一
  5. 解决办法:apt: 未找到命令
  6. vue弹出alert_vue.extend实现alert模态框弹窗步骤详解
  7. 雕刻机可以制作PCB
  8. 五类IP地址范围及作用
  9. 实现微信公众号跳转登录外部链接
  10. 计算机造句英语,电子计算机造句_造句大全
  11. 他捧红了王菲、张学友等近百个巨星,却甘心成为最普通的学佛人…
  12. 百度程序员开发避坑指南(前端篇)
  13. UIkit框架之轮播特效
  14. Java版本kafka-clients的介绍
  15. 如何自制圆环_如何自己动手制作环形变压器?
  16. java.sql.SQLException: The server time zone value‘xxxxxxxx‘ is unrecognized
  17. 怎样恢复被删除的文件
  18. 蚂蚁全媒体中心总编刘鑫炜:如何破解碎片化时代的营销困境
  19. 使用google浏览器添加并翻译英文无字幕视频
  20. C语言初级篇-----循环语句详解

热门文章

  1. 图论算法——无向图的邻接链表实现
  2. HarmonyOS IoT首著,走进万物互联的世界!
  3. 10分钟就能搭建远程开发环境?你早点怎么不出现(#`n´)!
  4. 利用算法识别车厘子与樱桃
  5. 产品经理应该扮演的几种角色
  6. 矩阵线性代数笔记整理汇总,超全面
  7. HTML - 布局div
  8. 深度学习:感知机perceptron
  9. python挖矿脚本_利用公共WiFi挖矿的Python脚本(注:仅作研究使用)
  10. Android sendEmptyMessage(0)里面的“0”是啥意思?