基础背包问题--0 1背包与完全背包

news2025/2/3 14:56:58

在这里插入图片描述

🎉🎉🎉写在前面:
博主主页:🌹🌹🌹戳一戳,欢迎大佬指点!
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个小菜鸟嘿嘿
-----------------------------谢谢你这么帅气美丽还给我点赞!比个心-----------------------------

在这里插入图片描述


基础背包问题

  • 0 1背包问题
  • 完全背包问题
  • 完全背包、0 1背包对比

例题来自于Acwing题库


0 1背包问题

特点:每件物品都有一定的价值,每件物品最多使用一次,在背包体积一定的情况下,求取所能取得的物品的价值最大化。


我们解决DP的问题,重点抓住两个点,状态的定义以及状态的计算(状态转移方程),下面以 0 1背包问题举例来进行分析:
在这里插入图片描述
在这里插入图片描述

这里要额外注意一个点,对于上面划分出的两个子集,包含第i个物品这一集不是一定存在的,必须满足背包容量j 要大于第i件物品的体积v ,也就是说j - v >= 0。

例题:
在这里插入图片描述

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();//物品种数
        int v = scan.nextInt();//背包体积
        //利用数组保存每个物品的价值和体积
        int[] V = new int[n + 1];//存储物品体积的数组
        int[] W = new int[n + 1];//存储物品价值的数组
        for(int i = 1;i <= n;i++){
            V[i] = scan.nextInt();
            W[i] = scan.nextInt();
        }
        
        int[][] dp = new int[n + 1][v + 1];
        //多出一行一列 当i = 0 j= 0 的时候最大价值应该都是0 所以也不需要进行特别的初始化
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= v;j++){
                dp[i][j] = dp[i-1][j];//不取第i件物品
                if(j >= V[i]){
                    //取第i件物品
                    dp[i][j] = Math.max(dp[i][j],dp[i-1][j - V[i]] + W[i]);
                }
            }
        }System.out.println(dp[n][v]);
    }
}

针对0 1背包问题进行优化:

对于0 1背包问题而言,我们还可以将其优化为一个一维的问题,也就是使用一个一维数组来进行计算。

没有优化之前,状态转移方程:F(i , j) = Max( F(i-1 , j) ,F(i-1 , j - V[i]) + W[i] ),从这里可以看到,其实F(i , j)是根据第 i-1件物品的状态值转移计算过来的,所以其实我们可以只使用一个一维数组,然后每一次的计算都在其前一个物品的所得到的一维数组上面进行计算修改就好了。你可以理解成原来是二维数组,现在每次计算都把上一行的值拷贝下来,然后在上面进行计算修改,这样整个过程就只涉及到一个一维数组,这样的数组也叫做滚动数组。最后的状态转移方程:F(j) = Max(F(j) , F( j - V[i]) + W[i]) (j表示的还是容量大小)

我们以上面的题来进行过程的分析:

体积价值
物品112
物品224
物品334
物品445

在这里插入图片描述

其实通过图示可以发现,说到底两种方法是没有区别的,只不过二维的方式是上一次的结果是单独在一行存储着,但是对于一维的解法来说,就只是在一个数组中进行修改,数组中每次就会保存着上次的结果。参照二维来讲,这里的F(j)就是F(i-1,j),F(j - V[i]) + W[i]就是F(i-1,j - V[i]) + W[i],在一个新的i的时候,数组中保存的就是上一次i-1的结果,所以这里可以从二维降到一维。

不过要注意,既然是只使用一个一维数组进行保存结果,然后下次会利用上次的值,所以不像二维那样,两次的运算数值之间不会存在有啥干扰,因为只有一个数组保存,你在这次计算的时候可能会把之前的值给覆盖掉,那么最后肯定是有问题的。对于这种会覆盖掉上一次的值的问题,我们的解决办法就是从后往前遍历,也就是F(5) -> F(0) 这么逆序的计算,就不会涉及到覆盖值的问题。

比如,正序遍历计算:
在这里插入图片描述

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();//物品数量
        int v = scan.nextInt();//背包体积
        //利用数组保存每个物品的价值和体积
        int[] V = new int[n + 1];//存储物品体积的数组
        int[] W = new int[n + 1];//存储物品价值的数组
        for(int i = 1;i <= n;i++){
            V[i] = scan.nextInt();
            W[i] = scan.nextInt();
        }
     
        int[] dp = new int[v+1];//浪费一个位置 让下标可以直接和背包容量对应起来
        for(int i = 1;i <= n;i++){
            for(int j = v;j >= V[i];j--){ //倒序计算的时候发现 j - V[i] < 0了就可以不用继续让j--计算了
                dp[j] = Math.max(dp[j] , dp[j - V[i]] + W[i]);
            }
        }
        System.out.println(dp[v]);
    }
}

完全背包问题

完全背包问题,相对于0 1背包问题,完全背包问题的特殊点就在于物品使用次数是不限制的,求的是在背包的体积一定的情况下,各种物品使用次数不限制,然后选取物品装入的价值最大化。

还是和上面一样,现在分析状态的定义以及状态计算:
在这里插入图片描述
在这里插入图片描述

例题:
在这里插入图片描述

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();//物品种数
        int v = scan.nextInt();//背包容量
        int[] V = new int[n+1];
        int[] W = new int[n+1];
        for(int i = 1;i <= n;i++){
            V[i] = scan.nextInt();
            W[i] = scan.nextInt();
        }
        
        int[][] dp = new int[n+1][v+1];
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= v;j++){
                for(int k = 0;k*V[i] <= j;k++){ //遍历枚举k的取值 也即是第i件物品使用次数 条件是不能超过了背包的容量
                    dp[i][j] = Math.max(dp[i][j] , dp[i-1][j - k*V[i]] + k*W[i]);
                }
            }
        }
        System.out.println(dp[n][v]);
    }
}

对于完全背包问题,上面的这种方法可以解决问题,但是三层for循环无疑时间复杂度有点高,下面我们将其进行优化:
在这里插入图片描述

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();//物品种数
        int v = scan.nextInt();//背包容量
        int[] V = new int[n+1];
        int[] W = new int[n+1];
        for(int i = 1;i <= n;i++){
            V[i] = scan.nextInt();
            W[i] = scan.nextInt();
        }
        
        int[][] dp = new int[n+1][v+1];
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= v;j++){
                //将dp[i][j] 优化后就只与两个状态值有关了 不需要再多加一层for循环
                dp[i][j] = dp[i-1][j];
                if(j - V[i] >= 0){
                    dp[i][j] = Math.max(dp[i][j],dp[i][j - V[i]] + W[i]);
                }
            }
        }
        System.out.println(dp[n][v]);
    }
}

同理0 1背包的问题,这里的二维自然也是可以降到一维的:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();//物品种数
        int v = scan.nextInt();//背包容量
        int[] V = new int[n+1];
        int[] W = new int[n+1];
        for(int i = 1;i <= n;i++){
            V[i] = scan.nextInt();
            W[i] = scan.nextInt();
        }
        
        int[] dp = new int[v+1];
        for(int i = 1;i <= n;i++){
            for(int j = V[i];j <= v;j++){ //循环直接从V[i]开始 可以减少掉一次if判断 最起码要放得下i才开始放
                dp[j] = Math.max(dp[j],dp[j - V[i]] + W[i]);
            }
        }
        System.out.println(dp[v]);
    }
}

完全背包、0 1背包对比

可能细心的同学已经发现了,在都优化为一维之后,可以发现两个问题的状态转移方程式是完全一样的,都是 dp[j] = Math.max(dp[j] , dp[j - V[i]] + W[i]) ,唯一一点不同的就是0 1背包问题计算的时候是逆序的遍历,但是完全背包问题是正序的遍历进行计算。
在这里插入图片描述

模拟计算过程如下,对于完全背包问题,我们对于每一个物品,我们求取的是在不超过当前背包容量的情况下,物品i使用不同次数的时候所能带来的最大价值的问题,所以在每一个i下,不同的j的时候,我们需要使用到的是它的新值,也就是i已经放入一定次数之后,有一个最大价值,然后我们在这个基础之上,再增加使用次数,再来求取一个最大价值。
在这里插入图片描述

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

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

相关文章

JS基于base64编码加密解密文本和图片

JS基于base64编码加密解密文本和图片 ​ 密码学&#xff0c;体系太庞大了&#xff0c;常见的加密解密算法很多&#xff0c;我仅了解了一下&#xff0c;这里仅介绍采用实现base64加密解密的方法。 严格地说base64不是加密算法&#xff0c;他只是一种编码方式&#xff0c;是一…

企业经营管理的核心是什么?

一、企业经营管理是什么&#xff1f; 企业经营管理通常是指&#xff0c;企业为了满足自身生存发展&#xff0c;通过对企业内部成员的经营活动进行计划、组织、协调、指挥、控制。企业经营管理主要目的是为了让企业在面向市场和用户是时&#xff0c;可以充分利用企业自身优势和…

excel日期函数:如何计算项目的开始和完成日期

制定工作计划是我们平时工作中经常会遇到的一类事务&#xff0c;例如某个项目&#xff0c;需要分成七个阶段来完成&#xff0c;已知项目的开始日期和每个项目需要的时间&#xff08;以天为单位&#xff09;&#xff0c;就可以做出一个项目的工作计划表&#xff1a; 需要重点强调…

无约束优化:修正阻尼牛顿法

文章目录无约束优化&#xff1a;修正阻尼牛顿法梯度法的困难经典牛顿法定义收敛性证明修正阻尼牛顿法考虑修正阻尼牛顿法的起因如何构造修正矩阵M参考文献无约束优化&#xff1a;修正阻尼牛顿法 梯度法的困难 无约束优化&#xff1a;线搜索最速下降 对于光滑函数而言&#x…

pg 锁机制深析

spin lock 使用 cas 去获取锁&#xff0c;先获取 spins_per_delay 次数&#xff0c;如果还失败&#xff0c;则每次获取失败将 delay 时长延长至 1~2倍 delay 值加 0.5 us&#xff0c;spins_per_delay 的值在获取锁后会做更新&#xff0c;如果这次没有等待&#xff0c;则下次可…

Python可视化——matplotlib.pyplot绘图的基本参数详解

目录 1.matplotlib简介 2.图形组成元素的函数用法 2.1. figure()&#xff1a;背景颜色 2.2 xlim()和 ylim()&#xff1a;设置 x&#xff0c;y 轴的数值显示范围 2.3 xlabel()和 ylabel()&#xff1a;设置 x&#xff0c;y 轴的标签文本 2.4 grid()&#xff1a;绘制刻度线的…

NVIDIA深度学习基础-理论与实践入门课程笔记及测验参考代码

1. 使用MNIST数据集进行图像分类 1.1 MNIST数据集 在深度学习的历史当中,对 MNIST 数据集里的70000张手写体数字的图像进行0到9的正确分类是一个重大的进展。如今,这个问题被认为是微不足道的,但是使用 MNIST 进行图像分类已经成为深度学习的一个 Hello World 练习。 以下…

TDC-GP30固件升级笔记

Bootloader介绍 系统重置或系统INIT发生后&#xff0c;总是请求引导加载程序。但是&#xff0c;只有在设置了引导加载器发布代码时&#xff0c;才会执行引导加载器操作。 Bootloader操作包括&#xff1a; “Register Configuration” 寄存器配置”&#xff0c;将配置数据传输…

8-Arm PEG-DBCO分子量决定外观性状,用于修饰生物分子

英文名称&#xff1a;8-Arm PEG-DBCO 中文名称&#xff1a;八臂-聚乙二醇-二苯基环辛炔 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k&#xff08;可按需定制&#xff09; 质量控制&#xff1a;95% 存储条件&#xff…

计算机毕设Python+Vue学校教务管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

计算机中丢失vcruntime140_1.dll,要怎么修复这个问题

关于计算机中丢失vcruntime140_1.dll其实是非常的常见的&#xff0c;当出现这种情况的时候&#xff0c;不需要担心&#xff0c;其实有好多种方法可以解决的。 解决丢失vcruntime140_1.dll的方法 第一种方法&#xff1a;既然计算机丢失了这个vcruntime140_1.dll文件&#xff0…

git:合并多个commit

目录1. 查看提交记录2. 合并commit3. 查看合并后的日志记录1. 查看提交记录 git log --oneline --graph2. 合并commit 这里希望合并最后3个commit&#xff08;modify1&#xff0c;modify2&#xff0c;modify3&#xff09; git rebase -i idid需要使用倒数第4个commit的id&am…

【docker】安装MySQL

实战&#xff1a;安装MySQL 思考&#xff1a;MySQL的数据持久化的问题 #获取镜像 docker pull mysql:5.7 #运行容器&#xff0c;需要做数据挂载 #安装启动MySQL&#xff0c;需要配置密码&#xff08;docker Hub&#xff09; #官方测试 docker run --name some-mysql -e MYSQL…

WebAPi实现多文件上传,并附带参数

需要实现的效果为&#xff0c;通过WebApi实现多文件上传功能&#xff0c;并且在上传时需要能附带文件说明参数&#xff0c;用于保存文件记录 –这里是需要的文件说明参数类 /// <summary>/// 前端文件上传时参数数据/// </summary>public class DistributionData{…

【Spring篇】基于xml的自动装配

&#x1f353;个人主页&#xff1a;个人主页 &#x1f352;系列专栏&#xff1a;SSM框架 自动装配&#xff1a; 根据指定的策略&#xff0c;在IOC容器中匹配某一个bean&#xff0c;自动为指定的bean中所依赖的类类型或接口类 型属性赋值 ①场景模拟 创建类UserControllerpublic…

29.图像卷积代码实现

1. 互相关运算 接下来&#xff0c;我们在corr2d函数中实现如上过程&#xff0c;该函数接受输入张量X和卷积核张量K&#xff0c;并返回输出张量Y。 import torch from torch import nn from d2l import torch as d2ldef corr2d(X,K): # X是输入&#xff0c;K是核矩阵计算二维互…

Cambridge IGCSE Chemistry 真题讲解1

考试局&#xff1a;Cambridge Assessment International Education (CAIE)考试类别&#xff1a;Cambridge International General Certificate of Secondary Education (IGCSE)考试科目&#xff1a;Chemistry考试单元&#xff1a;Paper 2 Multiple Choice (Extended)试卷代码&a…

什么是混合云、公有云、私有云?

很多用户都不清楚混合云、公有云、私有云的定义是什么&#xff0c;很多用户都不是很清楚具体的含义接下来一起跟着小编来看看吧。 一、混合云( hybrid cloud) 在混合云模式中&#xff0c;云平台由两种不同模式(私有或公有)云平台组合而成。这些平台依然是独立实体&#xff0c;但…

GIt远程仓库pull拉取代码

GIt远程仓库pull拉取代码 git教程可以参考之前的这篇文章使用git命令对gitee存放项目到仓库、切换分支以及合并分支教程 这篇文章主要说一下远程连接拉去并提交代码 如果你使用git clone <项目地址>拉取代码后更改无法提交的问题&#xff0c;可以使用以下解决方案 本地…

C++基础学习笔记(三)——核心编程PART1

参考链接&#xff1a;https://www.bilibili.com/video/BV1et411b73Z?p84&vd_sourceb4d9cee68649c8adcb1e266f7147cd5c 一、内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理…