01背包问题如何求解背包中的具体物品(JAVA)
引言:
01背包问题是比较基本的一种背包类型,起特征是背包内的物品只能取一次。
常规解法
对于常规解法就是构建一个二维的dp数组进行求解,主要问题在于选与不选
这里贴上完整的代码
private static int knapsack(int w, int n, int[] wt, int[] val) {
// 二维数组:状态定义:dp[i][j]表示从0-i个物品中选择不超过j重量的物品的最大价值
int[][] dp = new int[n + 1][w + 1];
int[][] g = new int[n + 1][w + 1];
//base case
// 初始化:第一列都是0,第一行表示只选取0号物品最大价值
for (int j = w; j >= wt[0]; j--) {
dp[0][j] = dp[0][j - wt[0]] + val[0];
// System.out.println(dp[0][j]);
g[0][j] = 1;
}
for (int i = 1; i < wt.length; i++) {
for (int j = 0; j <= w; j++) {
if (j < wt[i]) {
//当前背包装不下,只能选择不装入背包
dp[i][j] = dp[i - 1][j];
g[i][j] = 0;
} else {
//装入或者不装择优选择
// dp[i][j]=Math.max(dp[i-1][j-wt[i]]+val[i],dp[i-1][j]);
if (dp[i - 1][j - wt[i]] + val[i] >= dp[i - 1][j]) {
dp[i][j] = dp[i - 1][j - wt[i]] + val[i];
g[i][j] = 1;
} else {
dp[i][j] = dp[i - 1][j];
g[i][j] = 0;
}
}
}
}
int i = wt.length, V = w;
ListUtil.arrayTowNums(g);
System.out.println();
while (i >= 0) {
if (g[i][V] == 0) {
i--;
} else {
System.out.print(i + " ");
V -= wt[i];
i--;
}
}
return dp[n - 1][w];
}
特别要注意上面的g标记二维数组
其作用就是标记当某个物品被选择时,起就设为1,之后将添加的物品一个个取出
//装入或者不装择优选择
// dp[i][j]=Math.max(dp[i-1][j-wt[i]]+val[i],dp[i-1][j]);
if (dp[i - 1][j - wt[i]] + val[i] >= dp[i - 1][j]) {
dp[i][j] = dp[i - 1][j - wt[i]] + val[i];
g[i][j] = 1;
} else {
dp[i][j] = dp[i - 1][j];
g[i][j] = 0;
}
倒过去取出,判断标记数组g[i][j]的值
int i = wt.length, V = w;
ListUtil.arrayTowNums(g);
System.out.println();
while (i >= 0) {
if (g[i][V] == 0) {
i--;
} else {
System.out.print(i + " ");
V -= wt[i];
i--;
}
}
最后再来个压缩过空间的完整版本
private static int knapsack2(int w, int n, int[] wt, int[] val) {
int[] dp = new int[w + 1];
int[][] g = new int[n + 1][w + 1];
for (int i = 0; i <n; i++) {
for (int j = w; j >= wt[i]; j--) {
g[i][j]=0;
if (dp[j]<dp[j - wt[i]] + val[i]){
dp[j]=dp[j - wt[i]] + val[i];
g[i][j]=1;
}
}
}
int i = wt.length, V = w;
ListUtil.arrayTowNums(g);
System.out.println();
while (i >= 0) {
if (g[i][V] == 0) {
i--;
} else {
System.out.print(i + " ");
V -= wt[i];
i--;
}
}
return dp[w];
}