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

news/2024/9/22 23:38:24/

在这里插入图片描述

🎉🎉🎉写在前面:
博主主页:🌹🌹🌹戳一戳,欢迎大佬指点!
目标梦想:进大厂,立志成为一个牛掰的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.ppmy.cn/news/5232.html

相关文章

Nature Communications:人类丘脑的基因结构及其与十种常见大脑疾病的重叠

丘脑是位于大脑中心的重要交流中枢&#xff0c;由不同的核组成&#xff0c;对意识和高级皮层功能至关重要。丘脑结构和功能的改变涉及到常见的大脑疾病的发病机制&#xff0c;但丘脑的遗传结构仍然很大程度上未知。在这里&#xff0c;使用来自30114个个体的大脑扫描和基因型数据…

Fabric.js 修改画布交互方式到底有什么用?

本文简介 点赞 关注 收藏 学会了 fabric.js 为我们提供了很多厉害的方法。今天要搞明白的一个东西是 canvas.interactive 。 官方文档对 canvas.interactive 的解释是&#xff1a; Indicates that canvas is interactive. This property should not be changed. canvas.in…

LeetCode 321 周赛

2485. 找出中枢整数 给你一个正整数 n &#xff0c;找出满足下述条件的 中枢整数 x &#xff1a; 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数&#xff0c;则返回 -1 。题目保证对于给定的输入&#xff0c;至多存在一个中…

2022卡塔尔世界杯 | 我与足球的爱恨情仇

超燃世界杯&#xff0c;决战卡塔尔⚽我与足球在生活上的交集一、小学二、中学三、大学&#x1f4bb;我与足球在技术上的碰撞一、与足球有关的题目训练二、使用Java代码做一个足球小游戏&#x1f3c6;2022卡塔尔世界杯冠军 —— 阿根廷yyds一、球队比赛过程二、热门球员介绍三、…

HTTP报文详解

个人博客地址&#xff1a; http://xiaohe-blog.top/ 文章目录1. 请求1.1 请求行1.2 请求头1.3 空白行1.4 请求体2. 响应2.1 状态行2.2 响应头2.3 空白行2.4 响应体2.5 HTTP报文总结3. HTTP方法4. GET与POST的区别5. 状态码1. 请求 1.1 请求行 请求方式 请求地址 请求协议版本号…

第03讲:Redis的持久化方案

前言 redis是一个内存数据库&#xff0c;当redis服务器重启&#xff0c;获取电脑重启&#xff0c;数据会丢失&#xff0c;我们可以将redis内存中的数据持久化保存到硬盘的文件中。 redis提供两种持久化方式: RDB&#xff1a;快照&#xff0c;通过从服务器保存和持久化AOF&…

深度学习Week11-调用官方权重进行检测(YOLOv5)

前言&#xff1a; 很早之前&#xff0c;我发过小白YOLOv5全流程-训练实现数字识别_牛大了2022的博客-CSDN博客_yolov5数字识别这篇文章&#xff0c;里面用简练语言分享用yolov5训练自己的识别器&#xff0c;但包括我在内许多人仍不了解其运行原理&#xff1b;过去两周&#xff…

express路由模式

1.字符串模式路由路径 1.1“?” 匹配前面字符或子字符串0或1次 //可以访问acd,abcdapp.get(/ab?cd,(req,res)>{res.send(Hello) }) 1.2 “” 匹配前面字符或子字符串1或多次 //可以访问abcd,aabcd,等等 app.get(/abcd,(req,res)>{res.send(Hello) }) 1.3 “*” 匹…