01背包问题解法及优化
01背包问题
给定一个容量为C的背包,重量为weight,价值为value的石头,求背包能容纳的最大价值。
例
weight = [1, 2, 3]
value = [6, 10 ,12]
C = 5输出:22
背包中放入一个重量为3的石头和一个重量为2的石头
value = 10 + 12 = 22.
递归
public int findRes(int[] weight, int[] value, int capcity) {if(weight.length != value.length) return 0;if(capcity < 0)return 0;if(weight.length == 0)return 0;return helper(weight, value, capcity, weight.length - 1);}//f(n, c) 考虑[0 - n] 中背包重量为c的最优解//状态转移方程为 f(n, c) = Math.max(不考虑最后一个石头, 最后一个石头的价值 + 考虑最后一个石头后剩余背包容量的最大值);//状态转移方程为 f(n, c) = Math.max(f(n - 1, c), v[n] + f(n - 1, c - w[n]));private int helper(int[] weight, int[] value, int capcity, int index) {//如果背包中if(index < 0 || capcity <= 0)return 0;int res = helper(weight, value, capcity, index - 1);if(capcity >= weight[index])res = Math.max(res, value[index] + helper(weight, value, capcity - weight[index], index - 1));return res;}
不难发现此种解法会有重叠计算的问题
记忆化搜索-自下而上
我们开辟一个空间存储之前计算的结果
public int findRes2(int[] weight, int[] value, int capcity) {if(weight.length != value.length) return 0;if(capcity < 0)return 0;if(weight.length == 0)return 0;//行:是否考虑该位置的石头 0:只考虑第一个 1:考虑1 n:考虑n//列:背包剩余容量int memo[][] = new int[weight.length][capcity + 1];for(int i = 0; i < memo.length; i ++)Arrays.fill(memo[i], - 1);return helper2(weight, value, capcity, weight.length - 1, memo);}//f(n, c) 考虑[0 - n] 中背包重量为c的最优解//状态转移方程为 f(n, c) = Math.max(不考虑最后一个石头, 最后一个石头的价值 + 考虑最后一个石头后剩余背包容量的最大值);//状态转移方程为 f(n, c) = Math.max(f(n - 1, c), v[n] + f(n - 1, c - w[n]));private int helper2(int[] weight, int[] value, int capcity, int index, int[][] memo) {//如果背包中if(index < 0 || capcity <= 0)return 0;if(memo[index][capcity] != -1)return memo[index][capcity];int res = helper2(weight, value, capcity, index - 1, memo);if(capcity >= weight[index])res = Math.max(res, value[index] + helper2(weight, value, capcity - weight[index], index - 1, memo));memo[index][capcity] = res;return res;}
动态规划-自上而下
//时间复杂度为O(C * n) 其中C为背包容量,n为石头个数//空间复杂度为O(C * n) 其中C为背包容量,n为石头个数private int findRes3(int[] weight, int[] value, int capcity) {if(weight.length != value.length) return 0;if(capcity <= 0)return 0;if(weight.length == 0)return 0;//行:是否考虑该位置的石头 0:只考虑第一个 1:考虑1 n:考虑n//列:背包剩余容量int memo[][] = new int[2][capcity + 1];//base casefor(int i = 0; i <= capcity; i ++) {//只考虑第一个,只要容量满足,就可以放入if(weight[0] <= i)memo[0][i] = value[0];}for(int i = 1; i < weight.length; i ++) {for(int j = 0; j <= capcity; j ++) {int tmp = i ;//不考虑当前位置的石头memo[i][j] = memo[i - 1][j];//考虑且要放得下if(j >= weight[i])memo[i][j] = Math.max(memo[i][j], value[i] + memo[i - 1][j - weight[i]]);}}return memo[weight.length - 1][capcity];}
优化
可以发现i
行数据依赖于i - 1
行数据,所以我们只要重用空间就可以只使用2行记录状态。
//时间复杂度为O(C * n) 其中C为背包容量,n为石头个数//空间复杂度为O(C * 2) 其中C为背包容量private int findRes4(int[] weight, int[] value, int capcity) {if(weight.length != value.length) return 0;if(capcity <= 0)return 0;if(weight.length == 0)return 0;//行:是否考虑该位置的石头 0:只考虑第一个 1:考虑1 n:考虑n//列:背包剩余容量int memo[][] = new int[2][capcity + 1];//base casefor(int i = 0; i <= capcity; i ++) {//只考虑第一个,只要容量满足,就可以放入if(weight[0] <= i)memo[0][i] = value[0];}for(int i = 1; i < weight.length; i ++) {for(int j = 0; j <= capcity; j ++) {int tmp = i ;//不考虑当前位置的石头 [0]的位置放偶数 [1]的位置放奇数memo[i & 1][j] = memo[(i - 1) & 1][j];//考虑且要放得下if(j >= weight[i])memo[i & 1][j] = Math.max(memo[i & 1][j], value[i] + memo[(i - 1) & 1][j - weight[i]]);}}return memo[(weight.length - 1) & 1][capcity];}
最佳
在此深入可以发现,每一行数据只依赖上一行数据的左边数据,所以我们可以将数据存储实现在一行上。
//时间复杂度为O(C * n) 其中C为背包容量,n为石头个数//空间复杂度为O(C) 其中C为背包容量private int findRes5(int[] weight, int[] value, int capcity) {if(weight.length != value.length) return 0;if(capcity <= 0)return 0;if(weight.length == 0)return 0;//行:是否考虑该位置的石头 0:只考虑第一个 1:考虑1 n:考虑n//列:背包剩余容量int memo[] = new int[capcity + 1];//base casefor(int i = 0; i <= capcity; i ++) {//只考虑第一个,只要容量满足,就可以放入if(weight[0] <= i)memo[i] = value[0];}//右边数据依赖于左边数据,所以只要开辟O(C)空间for(int i = 1; i < weight.length; i ++) {for(int j = capcity; j >= weight[i]; j --) {memo[j] = Math.max(memo[j], value[i] + memo[j - weight[i]]);}}return memo[capcity];}
测试main函数
public static void main(String[] argc){int[] weight = new int[]{1, 2, 3};int[] value = new int[]{6, 10, 12};int res = new Pakage01().findRes(weight, value, 5);System.out.println(res);}
01背包问题解法及优化相关推荐
- 01背包问题关于空间优化的讨论
今天学习到了dp(其实以前就学过,不过没学好),再次看到背包问题的时候,突然意识到空间优化过后,他对剩余体积的遍历方式改变了! 看看背包九讲上的伪代码: 这是优化空间之前的 这是优化空间之后的.也是想 ...
- 0-1背包问题的一维数组优化解析
[问题描述] 常见的0-1背包问题,多使用二维数组来实现.二维数组实现时,常用的状态转移方程为: c[i][j]=c[i-1][j], j<vol[i] c[i][j]=max(c[i-1][j ...
- 01背包问题 动态规划求解方法 动态方程的详细解释 能理解的解释(附python代码)
01背包问题属于组合优化问题:假设你要出门旅游,你现在有一个书包,这个书包的容量(capacity)有限,有很多物品如牙刷.防晒霜.雨伞.水杯等等,但书包装不下所有物品,因此我们必须有所取舍.那么通常 ...
- 动态规划背包问题详解(二)---0-1背包问题
/** * 对于技术面试,你还在死记硬背么? * 快来"播"沙糖橘吧, * 用视频案例为你实战解读技术难点 * 聚焦Java技术疑难点,实战视频为你答疑解惑 * 越&qu ...
- 01背包问题和完全背包问题
内容仅供自己复习使用 整数划分 小A点菜 这两个问题十分类似,一个是01背包.一个是完全背包. 整数划分因为一个数字可以多选,所以是完全背包问题.点菜问题只能选或者不选,所以是01背包问题. 未优化版 ...
- 0-1背包问题(一维数组解法)
0-1背包问题: 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 这个问题的特点是:每种物品只 ...
- 0-1背包问题动态规划模型的Python解法
0-1背包问题动态规划模型的Python解法 1.01背包问题 2.Python解决方案 3.01背包问题例题 1.01背包问题 背包问题(Knapsack problem)是一种组合优化的NP完全问 ...
- 【动态规划笔记】01背包问题及优化
dp[i][j]:在前i件物品中选出若干件,放入容量为j的背包,能获得的最大价值 考虑第i件物品 [拿] 还是 [ 不拿 ] (1)j < c[i] 时,背包容量为j,而第i件物品重量大于j ...
- #动态规划 0-1背包问题空间复杂度优化
上一个版本的0-1背包代码的复杂度:时间复杂度O(n*C)空间复杂度O(n*C) 优化思路如下: 0-1背包问题: F(n,C)考虑将n个物品放入背包为C 的背包,使得价值最大. 状态转移方程:F(i ...
- 算法设计 - 01背包问题的状态转移方程优化,以及完全背包问题
01背包问题的一维状态转移方程的推导 前提摘要 前面这篇博客中: 算法设计 - 01背包问题_伏城之外的博客-CSDN博客 我们已经推导出了01背包问题的二维数组dp状态转移方程公式: 假设有N种不同 ...
最新文章
- centos7上搭建http服务器以及设置目录访问
- 【RocketMQ工作原理】indexFile
- 剑指 Offer 59 - I. 滑动窗口的最大值
- cisco路由器基本命令配置
- 检测msmq里消息的数量
- JS Math对象中一些小技巧
- 播放RTMP协议的流媒体的几种选择
- 你写的前端到底用没用到这些
- 冲刺阶段——Day2
- fir.im Weekly - 给 Mac 应用开发者的教程
- 小程序 | 保存图片时处理名称的细节(时间戳+后缀名)
- C++——必须使用复制构造函数的情况
- [POI2010]Divine Divisor
- mongodb 3.4 安装_【云服务器Centos7.x建站】MongoDB 3.4安装、使用、错误总结
- php strom 快捷键,PHPStorm常用快捷键总结
- c语言stdio函数大全,初学者常用的stdio库,原来还有这么多知识点
- selenium的三种等待方式
- seo的日常工作内容
- 关于边缘计算和边云协同,看这一篇就够了~
- 4月5号-4月11号