紧接前面一篇,讲一下“多重背包”问题,该问题与“完全背包”相比,在每个物品的选取次数上给出了限定,即选取次数k不能无限的增大,其方程和“完全背包”的极度相似,只是k的限定条件发生了变化。
c[i][j] = max(c[i-1][j-(k*w[i-1])] + k*v[i-1], c[i-1][j]) (0 <= k <= counts[i])
其中,counts[i]表示第i件物品可选取的次数。
假设:定义可容纳总重量W =10 Kg,物品种类 N = 4,每件物品重量w[i],对应价值v[i],求解在可容纳重量范围内如何选取可获最大价值。
本题具体题目:
可能出现的情况:
以上表格同样是从左到右、从上到下生成,具体实现代码如下:
//一维数组实现多重背包问题,注意其中的二分法划分
private static int[] BP_method03_1D(int m,int n,int[] w,int[] v,int[] counts){
int c[] = new int[m+1];
for (int i = 0; i < m+1; i++) {
c[i] = 0;//不必完全装满,则全部初始化为0
}
for (int i = 0; i < n; i++) {
//过滤掉次数小于2的物品
for(int k = 1; k < counts[i]; k <<= 1){
for (int j = m; j >= k * w[i]; j--) {//限定总重量
c[j] = Math.max(c[j-k*w[i]] + k*v[i], c[j]);
}
counts[i] -= k;
}
//此处为次数为1的物品
for (int j = m; j >= w[i]; j--) {//限定总重量
c[j] = Math.max(c[j-w[i]] + v[i], c[j]);
}
}
return c;
}
//二维数组实现,此处代码和前一篇“完全背包”二维数组方法中的源码基本一样,仅是k的取值由‘k*w[i-1] <= m’变为‘k <= counts[i]’
private static int[][] BP_method03_2D(int m,int n,int[] w,int[] v,int[] counts){
int c[][] = new int[n+1][m+1];
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n+1; i++) {
c[i][0] = 0;
}
for (int i = 0; i < m+1; i++) {
c[0][i] = 0;
}
for (int i = 1; i <= n; i++) {//限定物品种类,无重复
for (int j = 1; j <= m; j++) {//限定总重量
list.clear();
for (int k = 0; (k <= counts[i-1]) && (k >= 0); k++) {
if (j >= k*w[i-1]) {
list.add( Math.max(c[i-1][j-(k*w[i-1])] + k*v[i-1], c[i-1][j]));
}
}
c[i][j] = Collections.max(list);
}
}
return c;
}
其输出结果如下:
一维选取:
0 0 5 5 10 10 13 14 16 17 17
二维选取:
0 3 3 6 6 6 6 6 6 6
0 3 4 6 7 7 10 10 10 10
0 5 5 10 10 13 14 16 17 17
0 5 5 10 10 13 14 16 17 17