代码随想录-刷题第四十三天

1049. 最后一块石头的重量 II

题目链接:1049. 最后一块石头的重量 II

思路:本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成0-1背包问题了。与416. 分割等和子集非常相似。

动态规划五步曲:

  1. dp[j]:容量(其实就是重量)为j的背包,最多可以背最大重量为dp[j]。

    可以回忆一下0-1背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j]。

    相对于 0-1背包,本题中,石头的重量是 stones[i],石头的价值也是 stones[i] ,可以 “最多可以装的价值为 dp[j]” == “最多可以背的重量为dp[j]”

  2. 递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])

    0-1背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

  3. 初始化:0-1背包分割子集问题,dp数组的长度为物品总重量的一半。dp[0] = 0,因为重量都不会是负数,其他非0下标处也为0。

  4. 遍历顺序:先顺序遍历物品,再倒序遍历背包容量(防止重复放入)。

  5. 举例推导dp数组

    输入:[2,4,1,1],此时target = (2 + 4 + 1 + 1)/2 = 4 ,dp数组状态图如下:

    1049.最后一块石头的重量II

    最后dp[target]里是容量为target的背包所能背的最大重量。

    那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。

    在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的

class Solution {public int lastStoneWeightII(int[] stones) {// dp[j] 表示容量为j的背包,最多可以背最大重量为dp[j]。int sum = 0;for (int stone : stones) {sum += stone;}int target = sum / 2;// dp数组的长度为物品总重量的一半int[] dp = new int[target + 1];for (int i = 0; i < stones.length; i++) { // 遍历物品for (int j = target; j >= stones[i]; j--) { // 遍历背包dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);}}return sum - dp[target] - dp[target];}
}

494. 目标和

题目链接:494. 目标和

思路:假设加法的总和为x,那么减法对应的总和就是sum - x。所以我们要求的是 x - (sum - x) = target,进而x = (target + sum) / 2。此时问题就转化为,装满容量为x的背包,有几种方法。这里的x,就是bagSize,也就是我们后面要求的背包容量。

再回归到0-1背包问题,为什么是0-1背包呢?

因为每个物品(题目中的1)只用一次!

这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。

本题则是装满有几种方法。其实这就是一个组合问题了。

本题就变成了0-1背包求组合问题,动态规划五步曲:

  1. dp[j]:填满j(包括j)这么大容积的背包,有dp[j]种方法。

  2. 递推公式:dp[j] += dp[j - nums[i]]

    只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。

    例如:dp[j],j 为5,

    • 已经有一个1(即nums[i] = 1)的话,有 dp[4]种方法 凑成 容量为5的背包
    • 已经有一个2(即nums[i] = 2)的话,有 dp[3]种方法 凑成 容量为5的背包
    • 已经有一个3(即nums[i] = 3)的话,有 dp[2]中方法 凑成 容量为5的背包
    • 已经有一个4(即nums[i] = 4)的话,有 dp[1]中方法 凑成 容量为5的背包
    • 已经有一个5(即nums[i] = 5)的话,有 dp[0]中方法 凑成 容量为5的背包

    那么凑成dp[5]有多少方法呢,也就是把所有的 dp[j - nums[i]] 累加起来。

    这个递推公式在之后背包解决排列组合问题的时候还会用到!

  3. 初始化:dp[0] = 1

    因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。

  4. 遍历顺序:nums放在外循环,bagSize 在内循环,且内循环倒序。

  5. 举例推导dp数组

    输入:nums: [1, 1, 1, 1, 1], target: 3

    bagSize = (target + sum) / 2 = (3 + 5) / 2 = 4

    dp数组状态变化如下:

    img

class Solution {public int findTargetSumWays(int[] nums, int target) {// dp[j] 表示填满容量为j的背包,共有dp[j]种方法int sum = 0;for (int num : nums) {sum += num;}if ((target + sum) % 2 != 0) return 0; // 此时没有方案if (Math.abs(target) > sum) return 0;  // 此时没有方案// 背包的容量为nums里面需要相加的总值int bagSize = (target + sum) / 2;int[] dp = new int[bagSize + 1];dp[0] = 1;for (int i = 0; i < nums.length; i++) {for (int j = bagSize; j >= nums[i]; j--) {dp[j] += dp[j - nums[i]];}}return dp[bagSize];}
}

看到(target + sum) / 2 应该考虑计算的过程中向下取整有没有影响。

例如 sum 是5,target 是2的话,不能被2整除其实就是无解的;

同时如果 target 的绝对值已经大于 sum,sum 将无法满足

例如[1,1,1,1,1] target = 6,而 sum 最大只能达到5。

本题也可以用回溯法进行暴力搜索,但是执行时间会过长。如果仅仅是求个数的话,就可以用dp,但回溯算法:39. 组合总和要求的是把所有组合列出来,还是要使用回溯法的。


474. 一和零

题目链接:474. 一和零

思路:本题中strs 数组里的元素就是物品,每个物品都是一个!而 m 和 n 相当于是一个背包,两个维度的背包

本题依然是0-1背包问题,但是背包有两个维度,就是存放0的个数和存放1的个数,而不同长度的字符串就是不同大小的待装物品。

动态规划五步曲:

  1. dp[i][j]:最多由i个0和j个1的strs的最大子集的大小为dp[i][j]

  2. 递推公式:

    dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1)

    dp[i][j]可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。在遍历的过程中,取dp[i][j]的最大值。

    0-1背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    对比一下就会发现,字符串的zeroNumoneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。

    这就是一个典型的0-1背包! 只不过物品的重量有了两个维度而已。

  3. 初始化:dp[0][0] = 0

    因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。

  4. 遍历顺序:先顺序遍历物品,再倒序遍历背包容量。

    物品就是strs里的字符串,背包容量就是题目描述中的m和n。

  5. 举例推导dp数组

    以输入:[“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3为例

    最后dp数组的状态如下所示:

    474.一和零

class Solution {public int findMaxForm(String[] strs, int m, int n) {// dp[i][j] 代表最多由i个0和j个1的最大子集大小为dp[i][j]int[][] dp = new int[m + 1][n + 1];for (String str : strs) { // 遍历物品int zeroNum = 0, oneNum = 0;char[] chs = str.toCharArray();for (char ch : chs) {if (ch == '0') {zeroNum++;} else {oneNum++;}}// 遍历背包容量且从后往前遍历!for (int i = m; i >= zeroNum; i--) {for (int j = n; j >= oneNum; j--) {dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);}}}return dp[m][n];}
}

那个遍历背包容量的两层for循环先后顺序有没有什么讲究?

没讲究,都是物品重量的一个维度,先遍历哪个都行!


0-1背包总结

0-1背包有多种应用(不同维度上的应用):

  • 纯 0-1 背包是求给定背包容量,装满背包的最大价值是多少。
  • 416. 分割等和子集是求给定背包容量,能不能装满这个背包。
  • 1049. 最后一块石头的重量 II是求给定背包容量,尽可能装,最多能装多少。
  • 494. 目标和是求给定背包容量,装满背包有多少种方法。
  • 474. 一和零是求给定背包容量,装满背包最多有多少个物品。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

域传送漏洞

DNS解析 当用户访问域名时浏览器解析首先会查看浏览器缓存是否有对应的ip&#xff0c;如果没有则会到本地host文件中查看是否有对应的ip&#xff0c;如果没用则会将域名发送给本地区的DNS服务器. DNS服务器分为递归服务器&#xff0c;根服务器&#xff0c;权威服务器 首先是递…

C++初阶——基础知识(内联函数)

目录 1.内联函数 内联函数的示例代码 1.内联函数 是一种 C 中的函数定义方式&#xff0c;它告诉编译器在每个调用点上插入函数体的副本&#xff0c;而不是像普通函数那样在调用时跳转到函数体所在的地址执行。这样可以减少函数调用的开销&#xff0c;提高程序的执行效率。 …

0101包冲突导致安装docker失败-docker-云原生

文章目录 1 前言2 报错3 解决结语 1 前言 最近在学习k8s&#xff0c;前置条件就是要安装指定版本的docker&#xff0c;命令如下 yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io-1.4.62 报错 file /usr/libexec/docker/cli-plugins/docker-buildx fr…

git解决冲突场景

文章目录 git解决冲突场景 git解决冲突场景 假设我们在公司开发了一个功能修改了一个文件 我们现在模拟修改文件之后提交一个版本到本地&#xff0c;但是不上传到远程仓库 假设我们现在回到家开发代码&#xff0c;需要拉去最新的代码 提示已经更新。根本没有最新的代码改动&am…

【超图】SuperMap iClient3D for WebGL/WebGPU —— 坐标系位置 —— Cartesian2

作者&#xff1a;taco 说到关于地理必然逃不开位置的关系。借用百度百科的内容来说地理学&#xff08;geography&#xff09;&#xff0c;是研究地球表层空间地理要素或者地理综合体空间分布规律、时间演变过程和区域特征的一门学科。所以位置&坐标系必然逃不掉了。那么在S…

Cisco模拟器-OSPF路由协议

设计要求用两台双口路由器连接不同IP网段的计算机&#xff0c;并使用OSFP协议发现路由表使不同IP网段的计算机可以相互通信。 通过设计&#xff0c;可以连通IP地址网段不同的局域网&#xff0c;可应用在园区网的互连和互通的实现上。 主要配置步骤 路由器0&#xff1a; Router…

C# WPF上位机开发(以始为终,寻找真实的上位机需求)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 c# wpf、qt、mfc这些上位机的需求是真实存在的&#xff0c;在现实中有很多应用的地方&#xff0c;这一点大家都很清楚。而程序员本身呢&#xff0c…

OpenGauss 之索引查找和匹配

一. 前言 本文主要通过走读OpenGuass的代码&#xff0c;来了解查询的时候OpenGuass是如何查找表的索引信息以及根据谓词条件过滤掉无用的索引信息的。 二. 索引路径匹配流程 1. 首先OpenGuass在build_simple_rel的时候&#xff0c;首先将一个表以及与他相关的索引都加到rel-&g…

书摘:C 嵌入式系统设计模式 05

本书的原著为&#xff1a;《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》&#xff0c;讲解的是嵌入式系统设计模式&#xff0c;是一本不可多得的好书。 本系列描述我对书中内容的理解。 书中大量使用了 UML 表示法&#xff0…

FTP简介及搭建计算机端口的介绍

目录 一. FTP的简介 二. FTP的主要作用 三. 搭建FTP服务器 3.1 开启防火墙 3.2 创建组 3.3 创建用户 3.4 用户绑定组 3.5 安装FTP服务器 3.6 配置FTP服务器 3.7 配置FTP文件夹的权限 3.8 连接测试 3.8.1 服务器本机测试 3.8.2 外部服务器测试 3.8.3 借助工具MobalXterm 四…

7.12全排列②(LC47-M)

算法&#xff1a; 这道题目和46.全排列 (opens new window)的区别在与给定一个可包含重复数字的序列&#xff0c;要返回所有不重复的全排列。 所以就是多了个去重操作。 还是一样的套路&#xff1a; 先排序&#xff1a; Arrays.sort(nums); 再去重&#xff1a; // used[…

RainBond 构建组件 rbd-chaos 故障解决 【真实案例】

文章目录 背景分析官方排查说明尝试进一步分析解决参考背景 在 RainBond 中把所有组件都部署了至少 2 个实例后,开始出现构建/滚动更新直接报错,且没有日志(查看日志按钮点击后,里面啥也没有)。 然后再平台管理主界面,可以看到提示 rbd-chaos 组件故障: 分析 官方排…