Day42——Dp专题

news2025/3/1 3:33:59

文章目录

    • 五、多重背包
    • 六、背包问题总结
        • 动规五部曲
        • 背包递推公式
        • 遍历顺序
      • 18.打家劫舍
      • 19.打家劫舍II
      • 20.打家劫舍 III(dfs+缓存/树形DP)


五、多重背包

对于多重背包,我在力扣上还没发现对应的题目,所以这里就做一下简单介绍,大家大概了解一下。

有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci ,价值是Wi 。求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量,且价值总和最大。

多重背包和01背包是非常像的, 为什么和01背包像呢?

每件物品最多有Mi件可用,把Mi件摊开,其实就是一个01背包问题了。

例如:

背包最大重量为10。

物品为:

重量价值数量
物品01152
物品13203
物品24302

问背包能背的物品最大价值是多少?

和如下情况有区别么?

重量价值数量
物品01151
物品01151
物品13201
物品13201
物品13201
物品24301
物品24301

毫无区别,这就转成了一个01背包问题了,且每个物品只用一次。

多重背包就是将01背包的数量拆开

  • 01背包每个数量都是一个,多重背包数量可以为多个

Code

public void testMultiPack1(){
    // 版本一:改变物品数量为01背包格式
    List<Integer> weight = new ArrayList<>(Arrays.asList(1, 3, 4));
    List<Integer> value = new ArrayList<>(Arrays.asList(15, 20, 30));
    List<Integer> nums = new ArrayList<>(Arrays.asList(2, 3, 2));
    int bagWeight = 10;

    for (int i = 0; i < nums.size(); i++) {
        while (nums.get(i) > 1) { // 把物品展开为i
            weight.add(weight.get(i));
            value.add(value.get(i));
            nums.set(i, nums.get(i) - 1);
        }
    }

    int[] dp = new int[bagWeight + 1];
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = bagWeight; j >= weight.get(i); j--) { // 遍历背包容量
            dp[j] = Math.max(dp[j], dp[j - weight.get(i)] + value.get(i));
        }
        System.out.println(Arrays.toString(dp));
    }
}

public void testMultiPack2(){
    // 版本二:改变遍历个数
    int[] weight = new int[] {1, 3, 4};
    int[] value = new int[] {15, 20, 30};
    int[] nums = new int[] {2, 3, 2};
    int bagWeight = 10;

    int[] dp = new int[bagWeight + 1];
    for(int i = 0; i < weight.length; i++) { // 遍历物品
        for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
            // 以上为01背包,然后加一个遍历个数
            for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
                dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
            }
            System.out.println(Arrays.toString(dp));
        }
    }
}

六、背包问题总结

关于这几种常见的背包,其关系如下:

image-20221209111603255

动规五部曲

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

背包递推公式

问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); ,对应题目如下:

  • 动态规划:416.分割等和子集
  • 动态规划:1049.最后一块石头的重量 II

问装满背包有几种方法:dp[j] += dp[j - nums[i]] ,对应题目如下:

  • 动态规划:494.目标和
  • 动态规划:518. 零钱兑换 II
  • 动态规划:377.组合总和Ⅳ
  • 动态规划:70. 爬楼梯进阶版(完全背包

问背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); ,对应题目如下:

  • 动态规划:474.一和零

问装满背包所有物品的最小个数:dp[j] = min(dp[j - coins[i]] + 1, dp[j]); ,对应题目如下:

  • 动态规划:322.零钱兑换
  • 动态规划:279.完全平方数

遍历顺序

01背包

  • 二维数组遍历顺序先遍历物品还是先遍历背包都可以,遍历背包正序遍历
  • 一维数组必须先遍历物品之后遍历背包,遍历背包必须倒叙遍历

完全背包

  • 如果求组合数就是外层for循环遍历物品,内层for遍历背包;如果求排列数就是外层for遍历背包,内层for循环遍历物品。
  • 单纯的完全背包问题,遍历顺序先遍历物品还是先遍历背包都可以,遍历背包正序遍历

18.打家劫舍

力扣题目链接

思路

动规五部曲

  • 确定dp数组以及下标的含义

dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。

  • 确定递推公式

决定dp[i]的因素就是第i房间偷还是不偷。

如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。

如果不偷第i房间,那么dp[i] = dp[i - 1],即考虑i-1房,(注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点

然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);

  • dp数组如何初始化

从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]

从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);

	dp[0] = nums[0];
		dp[1] = Math.max(dp[0], nums[1]);
  • 确定遍历顺序

dp[i] 是根据dp[i - 2] 和 dp[i - 1] 推导出来的,那么一定是从前到后遍历!

for (int i = 2; i < nums.length; i++) {
			dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
		}
  • 举例推导dp数组

image-20221209122152699

红框dp[nums.size() - 1]为结果。

Code

// 动态规划
class Solution {
	public int rob(int[] nums) {
		if (nums == null || nums.length == 0) return 0;
		if (nums.length == 1) return nums[0];

		int[] dp = new int[nums.length];
		dp[0] = nums[0];
		dp[1] = Math.max(dp[0], nums[1]);
		for (int i = 2; i < nums.length; i++) {
			dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
		}

		return dp[nums.length - 1];
	}
}

19.打家劫舍II

力扣题目链接

思路

  • 情况一:考虑不包含首尾元素
  • 情况二:考虑包含首元素,不包含尾元素
  • 情况三:考虑包含尾元素,不包含首元素

而情况二 和 情况三 都包含了情况一了,所以只考虑情况二和情况三就可以了,套用打家劫舍Ⅰ

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if(nums == null || n == 0){
            return 0;
        }
        if(n == 1){
            return nums[0];
        }

        int res2 = fun(nums, 0, n - 2);// 情况2(含头不含尾)
        int res3 = fun(nums, 1, n - 1);// 情况3(含尾不含头)
        return Math.max(res3, res2);
    }

    // 打家劫舍I的处理方式
    public int fun(int[] nums, int l, int r){
        if(l == r) return nums[l];
        int[] dp = new int[nums.length + 10];
        dp[l] = nums[l];
        dp[l + 1] = Math.max(nums[l], nums[l + 1]);
        for(int i = l + 2; i <= r; i ++){
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
        }
        return dp[r];
    }
}

20.打家劫舍 III(dfs+缓存/树形DP)

题目链接:337. 打家劫舍 III - 力扣(LeetCode)

依旧是打家劫舍I的思考方式,对于每一个根节点有偷和不偷两种情况:

  • 偷根节点:那么它的左右儿子节点就不能偷了,那我们递归去偷它的左右子孙
  • 不偷根节点:那么它的左右儿子节点就能偷了,那我们递归去偷它的左右儿子即可

思路

递归三部曲和动规五部曲

  • 确定递归函数的参数和返回值

所以dp数组(dp table)以及下标的含义:下标为0记录不偷该节点所得到的的最大金钱,下标为1记录偷该节点所得到的的最大金钱。

所以本题dp数组就是一个长度为2的数组!

  • 确定终止条件

在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回

  • 确定遍历顺序

首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。

通过递归左节点,得到左节点偷与不偷的金钱。

通过递归右节点,得到右节点偷与不偷的金钱。

  • 确定单层递归的逻辑

如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; (如果对下标含义不理解就在回顾一下dp数组的含义

如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的,所以:val2 = max(left[0], left[1]) + max(right[0], right[1]);

最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}

  • 举例推导dp数组

image-20221209142200554

最后头结点就是 取下标0 和 下标1的最大值就是偷得的最大金钱

每一个节点是否选取,取决于它的左右子树是否选取

假设dp[0]:表示当前节点不选,dp[1]:表示选取当前节点

dpl[] dpr[]分别表示左右子树节点是否选取(dpl[1] dpr[1])和不选取(dpl[0] dpr[0])的最大值。

那么状态转移方程:

  • dp[0]dp[0] = max(dpl[0], dpl[1]) + max(dpr[0], dpr[1])
  • dp[1]dp[1] = root.val + dpl[0] + dpr[0]

最终答案:max(f[0], f[1]),选根和不选根的最大情况

之所以采用有序遍历是因为,根节点的情况取决于左右子树的情况,为此先要递归计算出左右子树!

Code

class Solution {
    public int rob(TreeNode root) {
        int[] dp = new int[2];
        dp = dfs(root);
        return Math.max(dp[0], dp[1]);
    }
    
    public int[] dfs(TreeNode root){
        if(root == null){
            return new int[]{0, 0};
        }
        
        // 分类讨论的标准是:当前结点偷或者不偷
        // 后续遍历
        // 由于需要后序遍历,所以先计算左右子结点,然后计算当前结点的状态值
        int[] dpl = dfs(root.left);
        int[] dpr = dfs(root.right);

        // 处理根
        int[] dp = new int[2];
        dp[0] = Math.max(dpl[0], dpl[1]) + Math.max(dpr[0], dpr[1]);// 偷根
        dp[1] = root.val + dpl[0] + dpr[0];// 不偷根
        return dp;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/75262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SpringMVC】上篇,超详细的教程带你学会SpringMVC

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;【Spring】 &#x1f96d;本文内…

java面向对象----抽象类

目录 抽象类与抽象方法 概念 抽象类应用 接 口 概念 接口的特点&#xff1a; 接口应用举例 Java 8中关于接口的改进 内部类 如何声明局部内部类 局部内部类的特点 匿名内部类 总结 抽象类与抽象方法 概念 随着继承层次中一个个新子类的定义&#xff0c;类变得越…

android OTA update

可以使用系統的API來實現系統更新。分兩種更新&#xff0c;non-streaming 和 streaming。non-streaming就是把更新包下載好&#xff0c;放到本地&#xff0c;然後執行更新。而streaming是爲了你的設備內存不夠&#xff0c;不能把更新包下載下來&#xff0c;使用的&#xff0c;u…

Helm 部署 java 项目到 K8S

文章目录部署流程模板目录文件解析DeploymentServiceIngress_helpers.tplChart.yamlvalues.yaml部署命令部署流程 准备 jar 包使用 Dockerfile 构建镜像上传镜像到仓库&#xff08;Harbor&#xff09;使用 Helm 模板部署 jar 到 K8S 本文着重讲解第四步&#xff0c;如何制作 …

后分库分表时代的数据库新选择:二维火搭载OceanBase再出发

如今&#xff0c;在中国任意走进一家餐饮商户&#xff0c;不论其规模大小&#xff0c;扫码点餐、自助点餐机、商家点餐小程序等已经基本成为标配。随着餐饮行业数智化持续加速推进&#xff0c;餐饮 SaaS 已经逐渐成为商户们的“必选题”&#xff0c;二维火便是这“必选题”之一…

深度解析 Git 是如何工作的?

深度解析 Git 是如何工作的&#xff1f;前言Git 的特性Git 实际上是如何工作的Commits 对象Tree 对象Blobs 对象总结分支创建与合并代码合并与冲突代码合并算法&#xff08;Myers&#xff09;图搜索代码 diff结尾参考&#xff1a;《Pro Git》、《Advanced Git》 前言 Git 是一…

如何实现工具无关化?关于自动化测试脚本的设计

1.问题的提出 最近几年来&#xff0c;我的自动化测试工具之旅大致是这样的&#xff0c;最早用的是QTP,然后是RFT(IBM的功能测试自动化产品),之后也经历了Selenium, Watir等&#xff0c;再后还是一些商业工具主要是偏web自动化及移动自动化&#xff0c;如sahi, appnium, Keynot…

你知道ArcGIS电子地图也有大字体地图吗(附下载方法)

概述 如果你经常使用水经微图&#xff0c;应该知道在水经微图内&#xff0c;百度电子地图和高德电子地图有大字体地图&#xff0c;最近我偶然发现ArcGIS电子地图也有大字体地图&#xff0c;这里给大家介绍一下下载方法。 加载地图 在ArcGIS中打开目录窗格&#xff0c;点击添…

五、卷积神经网络CNN8(不同卷积后图像大小计算)

类型划分 2 维卷积的计算分为了 3 类&#xff1a;1.full 2.same 3. valid 1、full蓝色为原图像&#xff0c;白色为对应卷积所增加的 padding&#xff0c;通常全部为 0&#xff0c;绿色是卷积后图片。图中的卷积的滑动是从卷积核右下角与图片左上角重叠开始进行卷积&#xff…

为什么企业需要实时跟踪进度的项目管理工具?

市场上的大多数 项目管理工具&#xff0c;都是垃圾进-垃圾出&#xff0c;这意味着如果你的团队没有输入正确甚至漏了输入他们活动的状态信息&#xff0c;就无法准确跟踪项目进展。 当选择了一个不能跟踪实时进度状态的项目管理工具&#xff0c;它不能给你提供准确报告进度所需…

大型复杂项目管理之风险预防

最近一个项目让项目经理小王焦头烂额&#xff0c;一问才知是第一次主导大型项目&#xff0c;各个维度的风险问题频发不断&#xff0c;项目感觉推动不下去了。例如&#xff0c;子项目进度延误、项目资源不足、项目交付物质量不过关等因素造成项目的整体延误等等。 项目案例信息&…

GoogLeNet 与 Inception

本篇主要介绍GoogLeNet,其被改进并应用在了YOLOV1目标检测算法中。 GoogLeNet是google推出的基于Inception模块的深度神经网络模型&#xff0c;在2014年的ImageNet竞赛中夺得了冠军&#xff0c;在随后的两年中一直在改进&#xff0c;形成了Inception V2、Inception V3、Incepti…

Java 将PDF转为Word

众所周知&#xff0c;PDF文档除了具有较强稳定性和兼容性外, 还具有较强的安全性&#xff0c;在工作中可以有效避免别人无意中对文档内容进行修改。但与此同时&#xff0c;也妨碍了对文档的正常修改。这时我们可以将PDF转为Word文档进行修改或再编辑。使用软件将 PDF 文档转换为…

Spring事务,浅谈!

目录 一、EnableTransactionManagement工作原理 二、Spring事务基本执行原理 三、Spring事务详细执行流程 四、Spring事务传播机制 五、Spring事务传播机制分类 六、Spring事务强制回滚 七、TransactionSynchronization 一、EnableTransactionManagement工作原理 开启Spr…

在C++中,为什么部分程序员喜欢在循环中写‘++i’而不是‘i++’?

自入行以来&#xff0c;无论是查阅资料、技术博客亦或是同事间的技术交流&#xff0c;都有一个共识:在循环的时候&#xff0c;务必使用前置操作符&#xff0c;因为其性能优于后置操作符&#xff0c;久而久之&#xff0c;这个就像一个不成文的规定&#xff0c;大家都在遵循&…

C++异常介绍

目录 一.异常 1.1C异常概念 1.2异常的使用 1.3异常和栈帧,重新抛出 二.异常体系 2.1自定义异常体系 2.2C标准库的异常体系 2.3异常规范 3.异常的优缺点 3.1优点 3.2缺点 一.异常 1.1C异常概念 语言传统的处理错误的方式&#xff1a; 1. 终止程序&#xff0c;如assert…

浮点类型的比较

浮点类型的比较一.浮点数精度的损失二.浮点数的比较1.方法一2.方法二3.方法三&#xff1a;系统方案一.浮点数精度的损失 关于浮点数的比较就不得不提到浮点数在内存中的存储&#xff0c;但这里篇幅太大&#xff0c;故我将其放在另一篇博客里&#xff0c;&#xff08;如果不了解…

laravel对于百万级别数据导出的一些经验

业务上的需求&#xff0c;我们开发的供应链系统某些业务表也陆续突破了百万级别。 原先使用 \Maatwebsite\Excel 插件导出的效率越来越慢&#xff0c;5w条数据导出基本要达到20min&#xff0c;甚至于30w数据导出基本上都超时。 为了解决这个问题&#xff0c;多种尝试&#xf…

AI 让观众成为 3D 版《老友记》的导演了?

《老友记》上线 3D 版了&#xff1f; 允许用户旋转镜头&#xff0c;且从近景切换到全景观看故事&#xff1f; 今年出炉的 3D 方向 AI 项目 SitCom3D&#xff0c;能够自动补齐《老友记》原剧中的三维拍摄空间&#xff0c;用户可以选择主视图、侧视图等不同角度欣赏剧集。镜头的…

[ vulhub漏洞复现篇 ] solr 远程命令执行 (CVE-2019-17558)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…