最近在做华为OJ题,做到“购物单”一题,突然感觉一脸懵逼,有点熟悉却又思维混乱,研究了一下,发现是由来已久的背包问题,遂详细的了解一下该算法。这里将最简单的背包问题——即无重复选取,每个物品仅放一次,其核心方程如下:
f[i][j] = max{f[i-1][j],f[i-1][j-[w[i-1]]]+v[i-1](j>=w[i-1])}
假设:定义可容纳总重量W =10 Kg,物品种类 N = 4,每件物品重量w[i],对应价值v[i],求解在可容纳重量范围内如何选取可获最大价值。
本题具体题目:
可能出现的背包情况:
上述表格从上到下,从左到右生成。
其中,每一行对应每一个种类,例如第一行,代表该背包中仅放a物品,所以当背包可容纳重量为0和1 Kg时,均无法放置a物品,总价值为0,而当背包容量为2 Kg时,则刚好容纳a物品,则其总价值为3,以此类推,生成该表格。
基于《背包九讲》,涉及该基础背包问题可以会出现这么两种情况:1,背包不需要完全装满,只需要宗价值最大化,此时应该使用一维数组,将c[i]全部初始化为0;2,要求背包恰好装满,在该情况下获取最大价值,此时,除此c[0]为0,其余则均初始化为无穷小。
其Java代码如下:
//一维数组,背包不必完全装满求最大价值
private static int[] BP_method01_1D(int m,int n,int[] w,int[] v){
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++) {//限定物品种类,无重复
for (int j = m; j >= w[i]; j--) {//限定总重量
c[j] = Math.max(c[j-w[i]] + v[i], c[j]);
}
}
return c;
}
//一维数组,背包恰好完全装满求最大价值
private static int[] BP_method01_1DC(int m,int n,int[] w,int[] v){
int c[] = new int[m+1];
for (int i = 1; i < m+1; i++) {
c[i] = Integer.MIN_VALUE;//必须完全装满,则除0以外均初始化为无穷小
}
for (int i = 0; i < n; i++) {//限定物品种类,无重复
for (int j = m; j >= w[i]; j--) {//限定总重量
c[j] = Math.max(c[j-w[i]] + v[i], c[j]);
}
}
return c;
}
// 01 基础背包问题,二维数组
private static int[][] BP_method(int m,int n,int[] w,int[] v){
int c[][] = new int[n+1][m+1];
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++) {//限定总重量
if (j >= w[i-1]) {
c[i][j] = Math.max(c[i-1][j-w[i-1]] + v[i-1], c[i-1][j]);
}else {
c[i][j] = c[i-1][j];
}
}
}
return c;
}
其输出结果如下:
一维不装满:
0 0 5 5 8 9 9 12 12 14 15
一维装满:
0 -2147483648 5 4 8 9 -2147483639 12 10 14 15
二维选取:
0 3 3 3 3 3 3 3 3 3
0 3 4 4 7 7 7 7 7 7
0 5 5 8 9 9 12 12 12 12
0 5 5 8 9 9 12 12 14 15