说明

前面已经介绍完了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多重背包算法,【动态规划】多重背包问题相关推荐

  1. java多重背包算法,01背包、完全背包和多重背包

    最优化原理 指的最优策略具有这样的性质:不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略. 如何证明一个最优策略的子策略也是最优解,一般使用反证法来证明. 无后效性 ...

  2. c++ 多重背包状态转移方程_背包问题之零钱兑换

    读完本文,你可以去力扣拿下如下题目: 518.零钱兑换II ----------- 零钱兑换 2 是另一种典型背包问题的变体,我们前文已经讲了 经典动态规划:0-1 背包问题 和 背包问题变体:相等子 ...

  3. 算法 动态规划 01背包问题

    01背包 问题分析 代码实现 从前往后拿,递归实现 非递归实现 非递归实现,自上向下填充 允接上一文章内容: 算法 动态规划: link. 问题分析 按照普通思维,首先想到应该为贪心算法,也就是计算每 ...

  4. java 0 1背包_浅谈java实现背包算法(0-1背包问题)

    0-1背包的问题 背包问题(Knapsack problem)是一种组合优化的NP完全问题.问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总 ...

  5. Java 01背包【动态规划·蓝桥杯练习题】(相信杨超越,相信锦鲤,默默努力,其它的看天意)

    锦鲤镇楼 1.题目描述: 时间限制:1.0s 内存限制:256.0MB 关键字:01背包 动态规划 问题描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高 ...

  6. 算法 - 动态规划(0-1背包问题)

    推出公式: 第三个就是:让上一次的和(新加入商品容量+(总空间-新加入的商品容量)也就是剩余空间的最大值,剩余空间的最大值去上一层找) package Algorithm.dac.knapsack;p ...

  7. python多重背包_【动态规划】多重背包问题

    说明 前面已经介绍完了01背包和完全背包,今天介绍最后一种背包问题--多重背包. 这个背包,听起来就很麻烦的样子.别慌,只要你理解了前面的两种背包问题,拿下多重背包简直小菜一碟. 如果没有看过前两篇0 ...

  8. 经典背包问题 01背包+完全背包+多重背包

    01 背包 有n 种不同的物品,每个物品有两个属性,size 体积,value 价值,现在给一个容量为 w 的背包,问最多可带走多少价值的物品. int f[w+1]; //f[x] 表示背包容量为x ...

  9. 【python】一篇讲透背包问题(01背包 完全背包 多重背包 二维费用背包)

    面对背包问题,有一个很重要的方程式:状态转移方程式 所以每一种背包问题我都会给出状态转移方程式 #01背包 什么是01背包型问题? 先给大家感受一下01背包型问题: 给定n种物品和一背包.物品i的重量 ...

最新文章

  1. linux怎么修改grub引导顺序,我如何更改GRUB引导顺序?
  2. 2020牛客国庆集训派对day4 Jokewithpermutation
  3. LeetCode 2096. 从二叉树一个节点到另一个节点每一步的方向(最小公共祖先)
  4. oracle基本的操作命令,oracle命令基本操作
  5. Go的nil切片与空切片一样吗?这么答,面试官让回去等信儿
  6. React中的纯组件
  7. timeSetEvent的用法(一)
  8. L2TP详解(五)——Client Initiated隧道和会话建立过程
  9. 设置和获取函数体现的软件工程
  10. 【VMware vSAN 7.0】5.4.2 创建 vSAN 集群—我们有软硬件解决方案
  11. zk的watcher机制
  12. Jetson nano安装Google拼音输入法
  13. ai人工智能对话了_对话人工智能模型
  14. 西门子S7-1200PLC堆栈程序 在使用西门子1200PLC时候发现,系统没有自带的堆栈功能块,不能实现数据的先进先出后进后出功能
  15. 零中频接收机频率转换图_【新品情报站】俄罗斯产VisAir HF DDC/DUC SDR 收发信机视频|接收机|转换器|ddc|天线|调谐器...
  16. vc中操作INI文件函数
  17. 【前端圭臬】二:你知道的和不知道的 HTML
  18. 魔兽世界怀旧服务器最新,魔兽世界怀旧服9月13日免费转服-11个转服新服务器一览...
  19. 伦茨科技蓝牙BLE5.2电子价签方案
  20. COSOSWAP:NFT的属性,能否改变游戏产业

热门文章

  1. d610网络计算机,智享手机电脑双模数据传输:CamFi 卡菲全能版 WIFI 传输器
  2. 大学期末不挂科速成课-史上最全
  3. html制作网页怎样做表单,(html网页表单制作.doc
  4. Win系统 - 手动设置开机启动项
  5. 由高铁故障谈软件缺陷管理和危机应对
  6. 智络会员管理软件的主要特点
  7. STM32F4移植FreeRTOS V10.3.1
  8. 计算机辅助海报设计,包装计算机辅助设计软件应用-Illustrator 综合实例 海报制作..pptx...
  9. PageHelper使用以及PageInfo中分页对象的转化
  10. 我和我的E影安全智能浏览器