0-1背包问题是一个经典的组合优化问题,通常使用动态规划算法来解决。在这个问题中,给定一组物品,每个物品都有一个重量和一个价值,以及一个固定容量的背包。目标是选择一些物品放入背包,使得它们的总重量不超过背包的容量,同时总价值最大。
下面是使用动态规划解决0-1背包问题的基本步骤:
-
定义状态:创建一个二维数组
dp,其中dp[i][j]表示在前i个物品中,背包容量为j时的最大总价值。这个数组的行表示物品的数量,列表示背包容量。 -
状态转移方程:定义状态转移方程,根据以下情况进行更新:
-
如果第i个物品的重量大于当前背包容量j,那么无法将该物品放入背包,所以
dp[i][j] = dp[i-1][j]。 -
否则,考虑两种情况,选择第i个物品或者不选择第i个物品,取两者中的最大值:
dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i]),其中weight[i]是第i个物品的重量,value[i]是第i个物品的价值。
-
-
初始化:初始化
dp数组的第一行和第一列,通常为0,表示没有物品或背包容量为0时的最大总价值。 -
填充DP表格:按照状态转移方程,从左上角向右下角逐步填充
dp数组。 -
最终结果:
dp[n][capacity]就是在n个物品中,背包容量为capacity时的最大总价值,其中n是物品的数量。 -
还原最优解(可选):如果需要找出具体的选择方案,可以倒推回去,从
dp[n][capacity]开始,根据状态转移方程的选择来确定哪些物品被放入背包。
/**
* 0-1 背包问题
* 1.n个物品都是固体,有重量和价值
* 2.现在你要取走不超过 10克 的物品
* 3.每次可以不拿或全拿,问最高价值是多少
* 编号 重量(g) 价值(元)
* 1 4 1600 黄金一块 A
* 2 8 2400 红宝石一枚 R
* 3 5 30 白银一块 S
* 0 1 1_000_000 钻戒一枚 D
*
* 0 1 2 3 4 5 6 7 8 9 10
* 0 0 0 0 0 A A A A A A A 黄金
* 1 0 0 0 0 A A A A R R R 红宝石
* 2 0 0 0 0 A A A A R R R 白银
* 3 0 D D D D DA DA DA DA DR DR 钻石
*/
public class KnapsackProblem {
static class Item {
//索引
int index;
//重量
int weight;
//价值
int value;
//名字
String name;
public Item(int index, int weight, int value, String name) {
this.index = index;
this.weight = weight;
this.value = value;
this.name = name;
}
}
static int select(Item[] items, int total) {
int[][] dp = new int[items.length][total + 1];
Item item0 = items[0];
for (int j = 0; j < total + 1; j++) {
if (j >= item0.weight) { //装的下
dp[0][j] = item0.value;
}else { //装不下
dp[0][j] = 0;
}
}
print(dp);
for (int i = 1; i < dp.length; i++) {
Item item = items[i];
for (int j = 0; j < total + 1; j++) {
if(j >= item.weight){ //装得下
dp[i][j] = Integer.max(dp[i-1][j],item.value + dp[i-1][j-item.weight]);
}else { //装不下
dp[i][j] = dp[i-1][j];
}
}
print(dp);
}
return dp[dp.length-1][total];
}
static void print(int[][] dp){
for (int i = 0; i < dp.length; i++) {
System.out.println(Arrays.toString(dp[i]));
}
}
public static void main(String[] args) {
Item[] items = new Item[]{
new Item(1, 4, 1600, "黄金"),
new Item(2, 8, 2400, "红宝石"),
new Item(3, 5, 30, "白银"),
new Item(4, 1, 1000000, "钻石"),
};
System.out.println(select(items, 10));
}
}