DP(3) | 0-1背包 | Java | 卡码 46 LeetCode 416 做题总结

news2024/11/15 11:15:29

代码随想录笔记
AcWing-背包九讲专题
一道例题
dd大牛背包9讲

背包笔记

对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。

01背包:n种物品,每种物品只有 1 个,每个物品只有选OR不选两种情况
完全背包:n种物品,每种物品有 无限 个,每个物品可选无限次
多重物品:n种物品,每种物品的个数各不相同,每个物品有限度
混合背包问题:
二位费用的背包问题:
分组背包

01 背包 :每种物品仅有一件,可以选择放或不放。

二维数组

题目:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

例子:背包最大重量为4
在这里插入图片描述

  • 暴力法
    每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是 o ( 2 n ) o(2^n) o(2n),这里的n表示物品数量。

  • 二维dp数组01背包(dp五步)

  • 首先对于整个问题:m个物品,背包容量最大为n。
    初步将问题分解为:在已经知道了前m-1个物品的所有最优解的情况下(即无论背包容量多少),再加上第m个物品的情况。
    此时有三种情况:
    1、第m个的重量超过了背包容量n,不可能放下第m个,所以此时的最优解,就是m-1个物品,背包容量为n的最优解。
    2、第m个的重量小于n,可以放但是不放,此时的最优解仍然是m-1个物品,背包容量为n的最优解。
    3、第m个的重量小于n,可以放而且真的放了,此时最优解就是第m个物品的价值,加上,m-1个物品时背包容量为(n-第m个物品的重量)的最优解。(第一点保证了第三点 j-weight[i]一定大于零)

根据递推关系发现,构造问题的最优解,不仅需要一个i来标识选择物品的范围,还需要一个j,来求出在0到i选择物品且背包容量为j时的最优解。从而将dp数组确定为dpij

(1)确定dp数组以及下标的含义
dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

(2)递推公式
不放物品i:dp[i-1][j]
放物品i : dp[i-1][j-weight[i]] + value[i]
(不放物品 i 时,背包重量 dp[i-1][j-weight[i]]
递推公式: dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])

j-weight[i]在干什么?在保证装入 物品 i 之前有充分的背包空间。
在这里插入图片描述

(3)初始化

  • 一个例题
    在这里插入图片描述
    初始化,
    首列:全为零,因为背包容量为0的时候,什么都装不了,价值为0
    首行:如果表示物品,只放该物品时的价值为value[物品](前提是背包的容量要够);如果不表示物品,初始化全为0
    其余全部初始化为0

(4)遍历顺序
对于二维数组实现01背包,双层for循环的条件哪个在外都可以

一维数组(滚动)

  • 以下面为例
    在这里插入图片描述
  • 代码
//代码随想录
    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        int bagWight = 4;
        testWeightBagProblem(weight, value, bagWight);
    }

    public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
        int wLen = weight.length;
        //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
        int[] dp = new int[bagWeight + 1];
        //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //打印dp数组
        for (int j = 0; j <= bagWeight; j++){
            System.out.print(dp[j] + " ");
        }
    }
  • debug打断点+手写
    在这里插入图片描述
    第二个for循环的条件j >= weight[i];直接保证当前i可以放进去
    逆序,为了之前数组的值不被污染。

dp[j] 容量为 j 的背包,拥有的最大价值为 dp[j]

  • 递推公式:dp[j] = max(dp[j]) ,dp[j-weight[i]] + value[i] )
    不放物品 i :dp[j] 【对比】二维数组中不放物品i是 dp[i-1][j]
    放物品i:dp[j-weight[i]] + value[i]
  • 初始化:dp[0] = 0
  • 遍历顺序:先物品后背包;且要倒序遍历背包。倒序遍历才能保证物品只添加一次

卡码 46. 携带研究材料

题目链接: https://kamacoder.com/problempage.php?pid=1046

  • AC-二维数组版
import java.util.*;

public class Main {
    
    public static void main(String[] args) {
        // 背包容量 N
        // 物品种类 M
        Scanner sc = new Scanner(System.in);
        
        int M = sc.nextInt();
        int N = sc.nextInt();
        
        int[] values = new int[M];
        int[] weights = new int[M];
        
        for(int i = 0; i < M;i++) {
            weights[i] = sc.nextInt();
        }
        
        
        for(int i = 0; i < M;i++) {
            values[i] = sc.nextInt();
        }
        
        int[][] dp = new int[M][N+1];
        
        // 初始化
        for(int i = weights[0]; i <= N; i++) {
            dp[0][i] = values[0];
        }
        
        // 先物品
        for(int i = 1; i < M; i++) {
            // 后背包
            for(int j = 0; j <= N; j++) {
                if(j-weights[i] < 0) {
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weights[i]] + values[i]);
                }
            }
        }
        System.out.println(dp[M-1][N]);
    }
}
  • 出错点
    初始化错误:背包初始化是 int[][] dp = new int[M][N+1]; 而不是 int[][] dp = new int[M][N]; ,因为 列代表 背包的容积是 0-N。
    所以返回值、初始化也有错
  • 这里的双层for循环哪个在外都可以。for物品 for背包容量,对于二维数组实现的0-1背包,是可以颠倒的。如下图,他的值是由左上方或者正上方得出来的

在这里插入图片描述

  • ac-滚动数组版
import java.util.*;

public class Main {
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int M = sc.nextInt();
        int N = sc.nextInt();
        int[]weight = new int[M];
        int[]value = new int[M];
        int[]dp = new int[N+1];
        
        for(int i=0; i<M; i++) {
            weight[i] = sc.nextInt();
        }
        
        for(int i=0; i<M; i++) {
            value[i] = sc.nextInt();
        }
        
        for(int i=0; i<M; i++) {
            for(int j=N; j>=weight[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j-weight[i]]+value[i]);
            }
        }
        System.out.println(dp[N]);
    }
}

416. 分割等和子集

请问这道题和动态规划有啥关系呢?

思路(卡哥的思路):
① 回溯,每个元素取或者不取,时间复杂度是指数级别的
②动态规划
在这里插入图片描述
以示例1为例,数组求总和22,和的一半11就是两个子集分别的和。
问容量为11的背包,能不能被[1,5,11,5]装满,如果装满了,就说明可以组成11,如果装不满,就说明不能组成11。如果能凑成11,那么剩下的元素肯定也能凑成另一个11。

背包问题的关键在元素可不可以重复使用,这里不可以,所以是0-1背包。每一个元素相当于一个物品,物品的重量和价值都是它的值。

  • 看了思路之后自己通过了
class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for(int i=0; i<nums.length; i++) {
            sum += nums[i];
        }
        if(sum % 2 != 0) return false;  //不可能是奇数,因为要划分成两个数组。

        // M = nums.length
        // N = sum/2
        // weight[i] = nums
        // value[i] = nums
        int M = nums.length;
        int N = sum/2;

        int[]dp = new int[N+1];
        for(int i=0; i<M; i++) {
            for(int j=N; j>=nums[i]; j--) {
                dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }

        if(dp[N] == N) {
            return true;
        } else {
            return false;
        }
    }
}
  • 补充 特判:如果是奇数,就不符合要求

Java

  • 导入
import java.util.*; 把util这个包下的全部类导入到程序中
  • 输入
Scanner sc = new Scanner(System.in);
        
int M = sc.nextInt();
int N = sc.nextInt();

int[] values = new int[M];
int[] weights = new int[M];

for(int i = 0; i < M;i++) {
    weights[i] = sc.nextInt();
}


for(int i = 0; i < M;i++) {
    values[i] = sc.nextInt();
}
  • 输出
System.out.println();
  • main函数为入口
public class Main {
    
    public static void main(String[] args) {
        // 
    }
}

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

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

相关文章

Linux 内核编译安装 - Deepin,Debian系

过程 下载 网站下载linux内核源码[^1] [^2]&#xff0c;并解压&#xff0c;进入源码目录&#xff1b; https://www.kernel.org/ https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/ 安装依赖 参考脚本安装依赖[^3]&#xff1b; sudo apt install git wget fakeroot build…

观察者模式的实现

引言&#xff1a;观察者模式——程序中的“通信兵” 在现代战争中&#xff0c;通信是胜利的关键。信息力以网络、数据、算法、算力等为底层支撑&#xff0c;在现代战争中不断推动感知、决策、指控等各环节产生量变与质变。在软件架构中&#xff0c;观察者模式扮演着类似的角色…

Vue和Element UI 路由跳转,侧边导航的路由跳转,侧边栏拖拽

首先看布局&#xff0c;因为我的用于页面显示的 <router-view> 是通过重定向定位到登陆页的&#xff0c;然后通过登陆页跳转到主页。项目中用到了点击侧边栏的跳转&#xff0c;所以记录下来&#xff0c;方便有需要的人用到~ 阐述 &#xff08;1&#xff09;.content{ di…

openharmony上传图片,并获取返回路径

适用条件&#xff1a; openharmony开发 4.0 release版本&#xff0c;对应能力API10 一直不断尝试&#xff0c;一会用官方提供的上传文件&#xff0c;一会用第三方库的axios都不行&#xff0c; 一会报错‘没权限&#xff0c;一会报错’路径错误&#xff0c;还有报错‘401参数错…

探索Java网络编程精髓:UDP与TCP的实战魔法!

Java 中提供了专门的网络编程程序包 java.net&#xff0c;提供了两种通信协议&#xff1a;UDP&#xff08;数据报协议&#xff09;和 TCP&#xff08;传输控制协议&#xff09;&#xff0c;本文对两种通信协议的开发进行详细介绍。 1 UDP 介绍 UDP&#xff1a;User Datagram Pr…

压缩pdf文件的大小,pdf档怎么压缩为最小内存

在现代工作和学习中&#xff0c;pdf文件已经成为了一种不可或缺的文件格式。它跨平台、保持格式不变的优势使其在文件传输和分享中占据了重要位置。然而&#xff0c;pdf文件往往因为包含大量图像和文本而体积较大&#xff0c;这给文件的传输和存储带来了不少困扰。本文将为你介…

不会编程怎么办?量化交易不会编程可以使用吗?

量化交易使用计算机模型程序代替人工进行交易&#xff0c;一般需要投资者自己编写程序建模&#xff0c;然后回测无误之后再进行实盘交易&#xff0c;那么不会编程的投资者能使用量化软件进行量化交易吗&#xff1f; 不会编程使用量化软件有两种方法 一种是请人代写代码&#x…

Java高频面试基础知识点整理8

干货分享&#xff0c;感谢您的阅读&#xff01;背景​​​​​​高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09; 最全文章见&#xff1a;Java高频面试基础知识点整理 &#xff08;一&#xff09;Java基础高频知识考点 针对人员&#xff1a; 1.全部人员都…

【三维AIGC】扩散模型LDM辅助3D Gaussian重建三维场景

标题&#xff1a;《Sampling 3D Gaussian Scenes in Seconds with Latent Diffusion Models》 来源&#xff1a;Glasgow大学&#xff1b;爱丁堡大学 连接&#xff1a;https://arxiv.org/abs/2406.13099 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何…

Guava LocalCache源码分析:LocalCache生成

Guava LocalCache源码分析&#xff1a;Cache生成 版本LocalCache参数说明Cache构建过程LocalCache介绍LocalCache实例化将builder中的属性赋值到LocalCache中分段 LocalCache为guava本地缓存的解决方案&#xff0c;提供了基于容量&#xff0c;时间和引用的缓存回收方式&#xf…

Spring MVC入门3

看完这篇博客你能学到什么 理解JSON的使用理解注解PathVariable理解解注解RequestPart理解cookie和Session的基本概念理解cookie和Session的区别 如果想真正掌握&#xff0c;还需要自己勤加练习。 正文 JSON JSON概念 JSON&#xff1a;JavaScript Object Notation 【JavaS…

免费长效IP在业务场景中的深度应用解析

在数字化竞赛的跑道上&#xff0c;每一秒的都至关重要&#xff0c;而免费长效IP&#xff0c;不仅为企业减少了运营成本&#xff0c;更在数据安全与访问效率上筑起了一道坚实的保护线。然而&#xff0c;面对市场上琳琅满目的代理服务&#xff0c;如何挑选出能应对各种业务场景的…

【JAVA poi-tl-ext 富文本转word】

富文本转word 环境使用poi-tl-ext的原因富文本转word代码 环境 jdk 1.8 <dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.4.16</version> </dependency>poi-tl-ext已经包…

ES 慢上游响应问题优化在用户体验场景中的实践

在抖音亿级日活流量的情况下&#xff0c;每天收到的用户反馈也是大量的&#xff0c;而用户反馈对于产品的发展与未来是至关重要的&#xff0c;因此用户体验管理平台&#xff08;简称VoC&#xff09;就应运而生&#xff0c;VoC 平台旨在通过技术平台化的方式&#xff0c;结合反馈…

字体反爬之自动化通过字体文件生成映射字典

1、首先找到以.ttf结尾的字体文件&#xff0c;下载下来&#xff0c;以我的字体文件sfont.ttf为例 sont.ttf下载地址https://download.csdn.net/download/lingyingdon/89534953 目前只测试了.ttf文件。如果想使用woff字体文件&#xff0c;请自行测试 2、下载分割字体文件的软件…

从汇编层看64位程序运行——参数传递的底层实现

大纲 小于等于6个参数一个参数总结 两个参数总结 三个参数总结 四个参数总结 五个参数总结 六个参数总结 大于6个参数七个参数总结 在32位系统中&#xff0c;参数的传递主要依靠栈来进行。那么64位系统上&#xff0c;是否依旧符合这个规则呢&#xff1f;答案是“不是”。64位系…

Objective-C 自定义渐变色Slider

文章目录 一、前情概要二、具体实现 一、前情概要 系统提供UISlider&#xff0c;但在开发过程中经常需要自定义&#xff0c;本次需求内容是实现一个拥有渐变色的滑动条&#xff0c;且渐变色随着手指touch的位置不同改变区域&#xff0c;类似如下 可以使用CAGradientLayer实现渐…

【Linux】Linux操作系统

Linux基本指令 os概念与定位 本节内容&#xff1a; Linux操作系统讲解 os概念与定位 操作系统&#xff08;Operating System&#xff0c;简称OS&#xff09;是管理和控制计算机硬件与软件资源的计算机程序。总的来讲&#xff0c;操作系统是一款做软硬件管理的软件。 了解操作…

springBoot(若依)集成camunda

1、下图为项目结构 2、最外层 pom引入依赖 <properties><!--camunda 标明版本&#xff0c;注意要个自己的Spring 版本匹配&#xff0c;匹配关系自行查询官网--><camunda.version>7.18.0</camunda.version> </properties> 3、common模块引入依赖 …

安卓14中Zygote初始化流程及源码分析

文章目录 日志抓取结合日志与源码分析systemServer zygote创建时序图一般应用 zygote 创建时序图向 zygote socket 发送数据时序图 本文首发地址 https://h89.cn/archives/298.html 最新更新地址 https://gitee.com/chenjim/chenjimblog 本文主要结合日志和代码看安卓 14 中 Zy…