代码改变世界

动态规划-01背包问题

2012-08-02 19:05  coodoing  阅读(1329)  评论(0)    收藏  举报

1、问题描述

给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大。在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。因此,该问题称为0-1背包问题。

2、算法分析

0-1背包问题的最优子结构,设(y1,y2,...,yn)是所给0-1背包问题的一个最优解,则(y2,y3,...,yn)是经过一次选择后的0-1背包问题的最优解。0-1背包问题的递归关系,设当前子问题的最优值为p[i][j],即p[i][j]表示前i个物品能装入载重量为j的背包中的最大价值。由0-1背包问题的最优子结构性质,可以建立计算p[i][j]的递归式:
若w[i]>j,第i个物品不装入背包;

否则,若w[i]<=j且第i个物品装入背包后的价值>p[i-1][j],则记录当前最大价值(替换为第i个物品装入背包后的价值)。

计算最大价值的动态规划算法

   1: for (i = 1; i < weight.length; i++) {
   2:             for (j = 1; j < max+1; j++) {
   3:                 //weight[i]>j,第i个物品不装入背包
   4:                 p[i][j] = p[i - 1][j];
   5:                 //weight[i]<=j,且第i个物品装入背包后的价值>value[i-1][j],则记录当前最大价值
   6:                 int temp;
   7:                 if(j >=weight[i])
   8:                 {
   9:                     temp = p[i - 1][j - weight[i]] + value[i];
  10:                     if (temp > p[i][j])
  11:                         p[i][j] = temp;
  12:                 }                
  13:             }
  14:         }        

即该段程序完成以下n个阶段:

1:只装入1个物品,确定在各种不同载重量的背包下,能够得到的最大价值;

2:装入2个物品,确定在各种不同载重量的背包下,能够得到的最大价值;

。。。

n:以此类推,装入n个物品,确定在各种不同载重量的背包下,能够得到的最大价值。

确定装入背包的具体物品,从p[n][max]向前逆推:

若p[n][max]>p[n-1][max],则第n个物品被装入背包,且前n-1个物品被装入载重量为max-w[n]的背包中

否则,第n个物品没有装入背包,且前n-1个物品被装入载重量为max的背包中

以此类推,直到确定第一个物品是否被装入背包为止。逆推代码如下:

   1: for (int i = len-1; i >0; i--) { 
   2:         if(p[i-1][temp]<p[i][temp])
   3:         {
   4:             selection[i] = true;    
   5:             temp-=weight[i];
   6:         }
   7:     }

3、算法实现

   1: public class DP_Knapsack01 {
   2:  
   3: int[] weight;
   4: int[] value;
   5: int max;
   6:  
   7: int[][] p;
   8:  
   9: boolean[] selection ; //背包是否被选择
  10:  
  11: public DP_Knapsack01() {
  12:     weight = new int[] {0, 16, 15, 15 };
  13:     value = new int[] {0, 45, 25, 25 };
  14:     max = 30;
  15:     selection = new boolean[weight.length];
  16:  
  17:     init();
  18: }
  19:  
  20: private void init() {
  21:     int row = weight.length;
  22:     int col = max+1 ;
  23:     int i, j;
  24:     
  25:     p = new int[row][col];
  26:     //初始化第0列
  27:     for(i=0;i<row;i++)
  28:         p[i][0]=0;
  29:     
  30:     // 表示初始时刻没装入物品时,最大价值为0
  31:     for(j=0;j<col;j++)
  32:         p[0][j]=0;
  33:     
  34: }
  35:  
  36: /*
  37:  * DP:选择与否的最优子结构性质
  38:  * p[i][j]表示前i个物品能装入载重量为j的背包中的最大价值
  39:  * p[i][j] = max{p[i-1][j],value[i]+p[i-1][j-weight[i]]}
  40:  * */
  41: /*
  42:  * 该段程序完成以下n个阶段:
  43:  1:只装入1个物品,确定在各种不同载重量的背包下,能够得到的最大价值
  44:  2:装入2个物品,确定在各种不同载重量的背包下,能够得到的最大价值
  45:    。。。
  46:  n:以此类推,装入n个物品,确定在各种不同载重量的背包下,能够得到的最大价值
  47:  * */
  48: private void knapsack() {
  49:     int i, j;
  50:     for (i = 1; i < weight.length; i++) {
  51:         for (j = 1; j < max+1; j++) {
  52:             //weight[i]>j,第i个物品不装入背包
  53:             p[i][j] = p[i - 1][j];
  54:             //weight[i]<=j,且第i个物品装入背包后的价值>value[i-1][j],则记录当前最大价值
  55:             int temp;
  56:             if(j >=weight[i])
  57:             {
  58:                 temp = p[i - 1][j - weight[i]] + value[i];
  59:                 if (temp > p[i][j])
  60:                     p[i][j] = temp;
  61:             }                
  62:         }
  63:     }        
  64: }
  65:  
  66: /* 求装入的物品
  67:  * 递推从value[n][max]向前逆推。
  68:  * 若p[n][max]>p[n-1][max],则第n个物品被装入背包,且前n-1个物品
  69:  * 被装入载重量为max-weight[n]的背包中。
  70:     否则,第n个物品没有装入背包,且前n-1个物品被装入载重量为max的背包中。
  71:  * */
  72: private void traceback()
  73: {
  74:     int temp = max;
  75:     int len = weight.length;
  76:     for (int i = len-1; i >0; i--) { 
  77:         if(p[i-1][temp]<p[i][temp])
  78:         {
  79:             selection[i] = true;    
  80:             temp-=weight[i];
  81:         }
  82:     }       
  83: }   
  84:  
  85: /**
  86:  * @param args
  87:  */
  88: public static void main(String[] args) {
  89:     // TODO Auto-generated method stub
  90:  
  91:     DP_Knapsack01 knap = new DP_Knapsack01();
  92:     knap.knapsack();
  93:     
  94:     knap.traceback();    
  95:     int len = knap.weight.length;
  96:     
  97:     System.out.println("动态规划对应的value值分别为:");
  98:     for(int i= 0;i<len;i++)
  99:     {
 100:         if(knap.selection[i])
 101:             System.out.print(knap.value[i]+" ");
 102:     }
 103:     System.out.println();
 104:     System.out.println("动态规划对应的weight值分别为:");
 105:     for(int i= 0;i<len;i++)
 106:     {
 107:         if(knap.selection[i])
 108:             System.out.print(knap.weight[i]+" ");
 109:     }
 110: }
 111:  
 112: }

参考资料:

http://blog.csdn.net/livelylittlefish/article/details/2186206