当前位置:   article > 正文

0-1背包问题的动态规划实现(java代码)_采用动态规划法实现0-1背包问题。wi 10 3 4 5 4 vi 24 2 9 10 9 将以下5

采用动态规划法实现0-1背包问题。wi 10 3 4 5 4 vi 24 2 9 10 9 将以下5个物品放

一,0-1背包问题

给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高

问题分析:面对每一个物品,我们只有选择拿取或者不拿两种选择,不能选择装入某物品的一部分,也不能装入同一物品多次。

0代表这个物品不拿,1代表这个物品拿。


例如:


 二,动态规划?

核心:将问题化为求解子问题,前一个子问题最值问题求解了,如果你找到子问题与当前问题的关系,那么当前问题就解决了,是一个迭代的过程。因此在0-1背包问题中,我们的核心步骤就是用表(通常用一个二维数组)记录子问题的解。

动态规划的步骤:问题结构分析,递推关系建立,自底向上计算,最优方案追踪。

最优子结构性质:问题的最优解由相关子问题最优解组合而成,子问题可以独立求解。

三,求解方案

1,暴力求解

通过枚举列出所有结果,然后进行比较


2,带备忘进行递归的暴力求解

 3,动态规划

 动态规划求解该问题的重点就在于列出这个表格(通过dp方程式,也就是状态转移方程式),最后求得最优解。

dp方程式(状态转移方程式):

设i为商品的序号,c为容量

if (c<v[ i ])

dp[ i ][ c ] = dp[ i - 1 ][ c ]

else

dp[ i ][ c ] = max (dp[ i-1 ][ c ] , dp[ i-1 ][ c - v[ i ]] + p[ i ])

  列表步骤

 最优解追踪:

回溯解决方案,倒叙判断是否选择商品,根据选择结果,确定最优子问题。

可以参考一下b站up主的视频(这里感谢一下up主,一键三连!!!)听懂不翻车系列之--背包问题(01背包 完全背包 多重背包 二维费用背包)

背包问题我们日常使用的话0-1和完全背包已经够用了,up主讲的很详细。

4,伪代码

 

5,求解思路

 

 

下面列出代码编译的过程。

按照上述伪代码流程稍作修改就行。


四,动态规划的代码构建(java)

1,代码框架

  1. public class KnapsackDP{
  2. public void DP(){
  3. }
  4. public void OutputDP(){
  5. }
  6. public static void main(String[] args){
  7. KnapsackDP Func = new KnapsackDP();
  8. int[] v={10,3,4,5,4};//背包容量
  9. int[] p={24,2,9,10,9};//商品价格
  10. int[][] dp=new int[p.length][13];//二维数组,横轴i表示商品序号,竖轴c表示的剩余容量//此处背包容量暂设为C=13
  11. Func.DP();
  12. }
  13. }

2,完整代码

  1. public class KnapsackDP{
  2. public void DP(int num,int C,int []vi,int []pi,int[][] dp,int[][] Rec){
  3. //初始化二维数组dp
  4. for(int i=0;i<=C;i++){
  5. dp[0][i]=0;
  6. }
  7. for(int i=0;i<=num;i++){
  8. dp[i][0]=0;
  9. }
  10. //开始求解二维数组dp
  11. for(int i=1;i<=num;i++){
  12. for(int c=1;c<=C;c++){
  13. if(c<vi[i]){
  14. //如果背包容量c小于第i个商品的容量vi,就拿该商品
  15. dp[i][c]=dp[i-1][c];
  16. Rec[i][c]=0;
  17. }else if(dp[i-1][c]>=( dp[i-1][c-vi[i]] + pi[i] )){
  18. //这个条件和下面的条件属于第二种情况,拿或者不拿商品
  19. //如果背包中已经拿的商品价值本身就比第i个商品的价值大,那么就不拿
  20. dp[i][c]=dp[i-1][c];
  21. Rec[i][c]=0;
  22. }else if(dp[i-1][c]<( dp[i-1][c-vi[i]] + pi[i] )){
  23. //如果第i个商品比背包中所有商品的价值都大,就扔掉背包中所有商品,拿这个第i个商品
  24. /*------具体可以参考一下dp方程的解释,在博客上---------*/
  25. dp[i][c]=dp[i-1][c-vi[i]]+pi[i];
  26. Rec[i][c]=1;
  27. }
  28. }
  29. }
  30. //输出代表价值的二维数组dp和用来回溯的二维数组Rec
  31. for(int i=0;i<=num;i++) {
  32. for (int c = 0; c <= C; c++) {
  33. System.out.print(dp[i][c] + "\t");
  34. }
  35. System.out.println();
  36. }
  37. System.out.println();
  38. for(int i=1;i<=num;i++) {
  39. for (int c = 0; c <= C; c++) {
  40. System.out.print(Rec[i][c] + " ");
  41. }
  42. System.out.println();
  43. }
  44. String[] name ={" ","啤酒","汽水","饼干","面包","牛奶"};//这里对商品名称定义一下
  45. OutputDP(num,C,vi,Rec,name);//这里开始回溯来查找具体拿了哪些商品
  46. //具体思路就是我们在已经求得最优解的情况下,对最优解进行倒叙回溯。
  47. }
  48. public void OutputDP(int num,int C,int []vi,int[][] Rec,String []name){
  49. int K=C;
  50. for(int i=num;i>=1;i--){
  51. if(Rec[i][K]==1){
  52. System.out.println("选择商品"+name[i]);
  53. K=K-vi[i];//从最优解开始减背包中商品的容量
  54. }else{
  55. System.out.println("不选择商品"+name[i]);
  56. }
  57. }
  58. }
  59. public static void main(String[] args){
  60. KnapsackDP Func = new KnapsackDP();
  61. int[] v={0,10,3,4,5,4};//背包容量
  62. int[] p={0,24,2,9,10,9};//商品价格
  63. int[][] dp=new int[p.length][13+1];//二维数组,横轴i表示商品序号,竖轴c表示的剩余容量//此处背包容量暂设为C=13
  64. int[][] Rec=new int[p.length][13+1];
  65. Func.DP(5,13,v,p,dp,Rec);//num为商品个数,C为背包容量
  66. }
  67. }


3,代码的注意点

出现了一次问题,在定义代表价值的二维数组dp和用来回溯商品的二维数组Rec时,数组下标越界了。一定要注意dp[ i ][ c ]和Rec[ i ][ c ] 中i和c的取值范围,还有对dp和Rec初始化后进行求解时的范围,严格按照背包容量和商品序号的范围进行定义。(这里可以参考我上面图片中手写的表,根据表格的数据对for循环进行定义)

另外,dp和Rec数组的i的取值范围也不一样,dp的i是从0到5一共6个数据,而Rec的i是从1到5一共5个数据,两个的c都是一样的总共13。

五,动态规划的特点

最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。

无后效性。即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。

子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。


动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式

这里根据我的理解,动态规划就是在分治法的基础上,解决分治法带来的冗余问题,避免重复计算,因此就有了通过表格来记录已解决的子问题的答案。

详情参考 动态规划_百度百科

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/432289
推荐阅读
相关标签
  

闽ICP备14008679号