算法设计与分析—多重背包问题

多重背包

内容:有n件物品和容量为m的背包 给出i件物品的重量以及价值 还有数量 求解让装入背包的物品重量不超过背包容量 且价值最大 

特点:每个物品都有了一定的数量

和零一背包以及完全背包相比,多重背包要难上一些,它的解法也非常多样。

最简单的方法当然就是将多重背包蜕化成零一背包来解决,比如一个物品最多可以拿N个,我们就把它拆成N种物品,这N种每种物品最多拿一个,相当于我们一种物品可以最多拿N个。但是有个很大的问题,就是复杂度。当然我们可以根据背包的体积做一些优化,比如当物品的数量很多并且超过了背包容量的时候,我们可以把超过容量的数量去掉,但是整体的复杂度还是很高。尤其是当我们背包容量很大的时候。

设f[j]表示重量不超过j公斤的最大价值 可得出状态转移方程 : 

             f[j]=max{f[j],f[j−k∗w[i]]+k∗v[i]} 

代码如下:

 1 // 多重背包:一维法
 2     public static int bag4(int W, int[] w, int[] v, int[] num) {
 3         int n = w.length - 1;// 第一个值,不算
 4         int[] f = new int[W + 1];
 5         for (int i = 1; i <= n; i++)
 6             for (int j = W; j >= w[i]; j--)
 7                 for (int k = 0; k <= num[i] && j - k * w[i] >= 0; k++) {
 8                     f[j] = Math.max(f[j], f[j - k * w[i]] + k * v[i]);
 9                 }
10  
11         return f[W]; // 最优解
12     }

二进制优化做法:

  首先,可以把它转化为一个01背包的问题。每个物品有s件,我们可以把它差分成s份,每份物品当做不同的个体,即只能选一次,这就转化为了01背包物品,但是这样的话,复杂度还是很大。

  继续优化,一个物品的数量是s的话,只要把s拆分成一些数字,使它们能够表示出1-s中任意一个数字,就可以,没必要把它拆成s个1。

  那么这样的数字最少需要多少个呢?最少需要log(s)个,向上取整。

  比如7,它最少需要3个数字来表示:

   即 1(2^0=1 ), 2(2^1=2), 4(2^2=4)。原因:每个数字有2种可能选或不选,那么可以表示的不同数字个数就是 2 * 2 * 2 = 8。但是还需要注意一个问题,就是有些数字可能能够表示出来一些大于s      的   数字,但是这件物品最多只有s件,那么就需要特殊处理一下最后一个数。
  比如10,若用1,2, 4, 8表示,可能会表示出来大于10的数字,例如:4+8=12。那么如果最后一个数字加上前面数的总和会大于s,就将它替换为剩下的物品个数,即将8替换为3,这时正好能表示出1-s所    有的数,-> 1, 2,4可以表示7以内的所有数,这些数加上3就可以表示10以内的所有数。

 1     // 多重背包:一维法-二进制优化
 2     public static int bag4(int W, int[] w, int[] v, int[] num) {
 3         int n = w.length;
 4         int[] f = new int[W + 1];
 5         int[] vv = new int[n*W];
 6         int[] ww = new int[n*W];
 7         // 复制w和v
 8         for(int i=0;i<n;i++)
 9         {
10             vv[i] = v[i];
11             ww[i] = w[i];
12         }
13         k = n;
14         for(int i=0;i<n;i++)
15         {
16             s = num[i]
17             for(int i=1;i<=s;i*=2)   //二进制优化
18                 vv[k]=v[i]*i,ww[k++]=w[i]*i,s-=i;
19             if(s>0)
20                 vv[k]=v[i]*s,ww[k++]=w[i]*s;
21         }
22  
23         for (int i = 1; i <= k; i++)  //01背包
24             for (int j = W; j >= ww[i]; j--)
25                         f[j] = Math.max(f[j], f[j - ww[i]] + vv[i]);
26  
27         return f[W]; // 最优解
28     }

 

 

 

posted on 2021-03-23 09:46  林岑  阅读(173)  评论(0)    收藏  举报

导航