java多重背包算法,【动态规划】多重背包问题
说明
前面已经介绍完了01背包和完全背包,今天介绍最后一种背包问题——多重背包。
这个背包,听起来就很麻烦的样子。别慌,只要你理解了前面的两种背包问题,拿下多重背包简直小菜一碟。
如果没有看过前两篇01背包和完全背包的文章,强烈建议先阅读一下,因为本文跟前两篇文章关联性很强。
多重背包
有N种物品和一个容量为T的背包,第i种物品最多有M[i]件可用,价值为P[i],体积为V[i],求解:选哪些物品放入背包,可以使得这些物品的价值最大,并且体积总和不超过背包容量。
对比一下完全背包,其实只是多了一个限制条件,完全背包问题中,物品可以选择任意多件,只要你装得下,装多少件都行。
但多重背包就不一样了,每种物品都有指定的数量限制,所以不是你想装,就能一直装的。
举个栗子:有A、B、C三种物品,相应的数量、价格和占用空间如下图:
跟完全背包一样,贪心算法在这里也不适用,我就不重复说明了,大家可以回到上一篇中看看说明。
递归法
还是用之前的套路,我们先来用递归把这个问题解决一次。
用ks(i,t)表示前i种物品放入一个容量为t的背包获得的最大价值,那么对于第i种物品,我们有k种选择,0 <= k <= M[i] && 0 <= k * V[i] <= t,即可以选择0、1、2…M[i]个第i种物品,所以递推表达式为:
ks(i,t) = max{ks(i-1, t - V[i] * k) + P[i] * k}; (0 <= k <= M[i] && 0 <= k * V[i] <= t)
同时,ks(0,t)=0;ks(i,0)=0;
对比一下完全背包的递推关系式:
ks(i,t) = max{ks(i-1, t - V[i] * k) + P[i] * k}; (0 <= k * V[i] <= t)
简直一毛一样,只是k多了一个限制条件而已。
使用上面的栗子,我们可以先写出递归解法:
public static class MultiKnapsack {
private static int[] P={0,2,3,4};
private static int[] V={0,3,4,5};
private static int[] M={0,4,3,2};
private static int T = 15;
@Test
public void soleve1() {
int result = ks(P.length - 1,T);
System.out.println("最大价值为:" + result);
}
private int ks(int i, int t){
int result = 0;
if (i == 0 || t == 0){
// 初始条件
result = 0;
} else if(V[i] > t){
// 装不下该珠宝
result = ks(i-1, t);
} else {
// 可以装下
// 取k个物品i,取其中使得总价值最大的k
for (int k = 0; k <= M[i] && k * V[i] <= t; k++){
int tmp2 = ks(i-1, t - V[i] * k) + P[i] * k;
if (tmp2 > result){
result = tmp2;
}
}
}
return result;
}
}
同样,这里的数组P/V/M分别添加了一个元素0,是为了减少越界判断而做的简单处理,运行如下:
最大价值为:11
对比一下完全背包中的递归解法:
private int ks(int i, int t){
int result = 0;
if (i == 0 || t == 0){
// 初始条件
result = 0;
} else if(V[i] > t){
// 装不下该珠宝
result = ks(i-1, t);
} else {
// 可以装下
// 取k个物品i,取其中使得总价值最大的k
for (int k = 0; k * V[i] <= t; k++){
int tmp2 = ks(i-1, t - V[i] * k) + P[i] * k;
if (tmp2 > result){
result = tmp2;
}
}
}
return result;
}
仅仅多了一个判断条件而已,所以只要弄懂了完全背包,多重背包就不值一提了。
最优化原理和无后效性的证明跟多重背包基本一致,所以就不重复证明了。
动态规划
参考完全背包的动态规划解法,就很容易写出多重背包的动态规划解法。
自上而下记忆法
ks(i,t) = max{ks(i-1, t - V[i] * k) + P[i] * k}; (0 <= k <= M[i] && 0 <= k * V[i] <= t)
public static class MultiKnapsack {
private static int[] P={0,2,3,4};
private static int[] V={0,3,4,5};
private static int[] M={0,4,3,2};
private static int T = 15;
private Integer[][] results = new Integer[P.length + 1][T + 1];
@Test
public void solve2() {
int result = ks2(P.length - 1,T);
System.out.println("最大价值为:" + result);
}
private int ks2(int i, int t){
// 如果该结果已经被计算,那么直接返回
if (results[i][t] != null) return results[i][t];
int result = 0;
if (i == 0 || t == 0){
// 初始条件
result = 0;
} else if(V[i] > t){
// 装不下该珠宝
result = ks2(i-1, t);
} else {
// 可以装下
// 取k个物品,取其中使得价值最大的
for (int k = 0; k <= M[i] && k * V[i] <= t; k++){
int tmp2 = ks2(i-1, t - V[i] * k) + P[i] * k;
if (tmp2 > result){
result = tmp2;
}
}
}
results[i][t] = result;
return result;
}
}
这里其实只是照葫芦画瓢。
自下而上填表法
同样也可以使用填表法来解决,此时需要将数组P、V、M额外添加的元素0去掉。
除了k的限制不一样之外,其他地方跟完全背包的解法完全一致:
public static class MultiKnapsack {
private static int[] P={2,3,4};
private static int[] V={3,4,5};
private static int[] M={4,3,2};
private static int T = 15;
private int[][] dp = new int[P.length + 1][T + 1];
@Test
public void solve3() {
for (int i = 0; i < P.length; i++){
for (int j = 0; j <= T; j++){
for (int k = 0; k <= M[i] && k * V[i] <= j; k++){
dp[i+1][j] = Math.max(dp[i+1][j], dp[i][j-k * V[i]] + k * P[i]);
}
}
}
System.out.println("最大价值为:" + dp[P.length][T]);
}
}
跟01背包问题一样,完全背包的空间复杂度也可以进行优化,具体思路这里就不重复介绍了,可以翻看前面的01背包问题优化篇。
优化后的状态转移方程为:
ks(t) = max{ks(t), ks(t - Vi) + Pi}
public static class MultiKnapsack {
private static int[] P={2,3,4};
private static int[] V={3,4,5};
private static int[] M={4,3,2};
private static int T = 15;
private int[] newResults = new int[T + 1];
@Test
public void resolve4() {
int result = ksp(P.length,T);
System.out.println(result);
}
private int ksp(int i, int t){
// 开始填表
for (int m = 0; m < i; m++){
// 考虑第m个物品
// 分两种情况
// 1: M[m] * V[m] > T 则可以当做完全背包问题来处理
if (M[m] * V[m] >= T) {
for (int n = V[m]; n <= t ; n++) {
newResults[n] = Math.max(newResults[n], newResults[n - V[m]] + P[m]);
}
} else {
// 2: M[m] * V[m] < T 则需要在 newResults[n-V[m]*k] + P[m] * k 中找到最大值,0 <= k <= M[m]
for (int n = V[m]; n <= t ; n++) {
int k = 1;
while (k < M[m] && n > V[m] * k ){
newResults[n] = Math.max(newResults[n], newResults[n - V[m] * k] + P[m] * k);
k++;
}
}
}
// 可以在这里输出中间结果
System.out.println(JSON.toJSONString(newResults));
}
return newResults[newResults.length - 1];
}
}
输出如下:
[0,0,0,0,2,2,2,4,4,4,6,6,6,8,8,8]
[0,0,0,0,2,3,3,4,5,6,6,7,8,9,9,10]
[0,0,0,0,2,3,4,4,5,6,7,8,8,9,10,11]
11
这里有一个较大的不同点,在第二层循环中,需要分两种情况考虑,如果 M[m] * V[m] >= T ,那么第m个物品就可以当做完全背包问题来考虑,而如果 M[m] * V[m] < T,则每次选择时,需要从 newResults[n-V[m]*k] + P[m] * k(0 <= k <= M[m])中找到最大值。
代码很简单,但要理解却并不容易,为了加深理解,再画一张图:
多重背包问题同样也可以转化成01背包问题来求解,因为第i件物品最多选 M[i] 件,于是可以把第i种物品转化为M[i]件体积和价值相同的物品,然后再来求解这个01背包问题。
总结
多重背包问题跟完全背包简直如出一辙,仅仅是比完全背包多一个限制条件而已,如果你回过头去看看前一篇文章,就会发现这篇文章简直就是抄袭。。
关于多重背包问题的解析到此就结束了,三个经典的背包问题到这里就告一段落了。
如果有疑问或者有什么想法,也欢迎关注我进行留言交流:
java多重背包算法,【动态规划】多重背包问题相关推荐
- java多重背包算法,01背包、完全背包和多重背包
最优化原理 指的最优策略具有这样的性质:不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略. 如何证明一个最优策略的子策略也是最优解,一般使用反证法来证明. 无后效性 ...
- c++ 多重背包状态转移方程_背包问题之零钱兑换
读完本文,你可以去力扣拿下如下题目: 518.零钱兑换II ----------- 零钱兑换 2 是另一种典型背包问题的变体,我们前文已经讲了 经典动态规划:0-1 背包问题 和 背包问题变体:相等子 ...
- 算法 动态规划 01背包问题
01背包 问题分析 代码实现 从前往后拿,递归实现 非递归实现 非递归实现,自上向下填充 允接上一文章内容: 算法 动态规划: link. 问题分析 按照普通思维,首先想到应该为贪心算法,也就是计算每 ...
- java 0 1背包_浅谈java实现背包算法(0-1背包问题)
0-1背包的问题 背包问题(Knapsack problem)是一种组合优化的NP完全问题.问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总 ...
- Java 01背包【动态规划·蓝桥杯练习题】(相信杨超越,相信锦鲤,默默努力,其它的看天意)
锦鲤镇楼 1.题目描述: 时间限制:1.0s 内存限制:256.0MB 关键字:01背包 动态规划 问题描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高 ...
- 算法 - 动态规划(0-1背包问题)
推出公式: 第三个就是:让上一次的和(新加入商品容量+(总空间-新加入的商品容量)也就是剩余空间的最大值,剩余空间的最大值去上一层找) package Algorithm.dac.knapsack;p ...
- python多重背包_【动态规划】多重背包问题
说明 前面已经介绍完了01背包和完全背包,今天介绍最后一种背包问题--多重背包. 这个背包,听起来就很麻烦的样子.别慌,只要你理解了前面的两种背包问题,拿下多重背包简直小菜一碟. 如果没有看过前两篇0 ...
- 经典背包问题 01背包+完全背包+多重背包
01 背包 有n 种不同的物品,每个物品有两个属性,size 体积,value 价值,现在给一个容量为 w 的背包,问最多可带走多少价值的物品. int f[w+1]; //f[x] 表示背包容量为x ...
- 【python】一篇讲透背包问题(01背包 完全背包 多重背包 二维费用背包)
面对背包问题,有一个很重要的方程式:状态转移方程式 所以每一种背包问题我都会给出状态转移方程式 #01背包 什么是01背包型问题? 先给大家感受一下01背包型问题: 给定n种物品和一背包.物品i的重量 ...
最新文章
- linux怎么修改grub引导顺序,我如何更改GRUB引导顺序?
- 2020牛客国庆集训派对day4 Jokewithpermutation
- LeetCode 2096. 从二叉树一个节点到另一个节点每一步的方向(最小公共祖先)
- oracle基本的操作命令,oracle命令基本操作
- Go的nil切片与空切片一样吗?这么答,面试官让回去等信儿
- React中的纯组件
- timeSetEvent的用法(一)
- L2TP详解(五)——Client Initiated隧道和会话建立过程
- 设置和获取函数体现的软件工程
- 【VMware vSAN 7.0】5.4.2 创建 vSAN 集群—我们有软硬件解决方案
- zk的watcher机制
- Jetson nano安装Google拼音输入法
- ai人工智能对话了_对话人工智能模型
- 西门子S7-1200PLC堆栈程序 在使用西门子1200PLC时候发现,系统没有自带的堆栈功能块,不能实现数据的先进先出后进后出功能
- 零中频接收机频率转换图_【新品情报站】俄罗斯产VisAir HF DDC/DUC SDR 收发信机视频|接收机|转换器|ddc|天线|调谐器...
- vc中操作INI文件函数
- 【前端圭臬】二:你知道的和不知道的 HTML
- 魔兽世界怀旧服务器最新,魔兽世界怀旧服9月13日免费转服-11个转服新服务器一览...
- 伦茨科技蓝牙BLE5.2电子价签方案
- COSOSWAP:NFT的属性,能否改变游戏产业
热门文章
- d610网络计算机,智享手机电脑双模数据传输:CamFi 卡菲全能版 WIFI 传输器
- 大学期末不挂科速成课-史上最全
- html制作网页怎样做表单,(html网页表单制作.doc
- Win系统 - 手动设置开机启动项
- 由高铁故障谈软件缺陷管理和危机应对
- 智络会员管理软件的主要特点
- STM32F4移植FreeRTOS V10.3.1
- 计算机辅助海报设计,包装计算机辅助设计软件应用-Illustrator 综合实例 海报制作..pptx...
- PageHelper使用以及PageInfo中分页对象的转化
- 我和我的E影安全智能浏览器