动态规划之背包问题详解
说明
- 动态规划也是一种将复杂问题拆解为许多子问题的一种算法,与分治算法类似,只不过动态规划拆解后的子问题是互相有关联的,而分治拆解后的子问题彼此没有关联
- 背包问题是指有一堆物品,每个物品都有自己的价格,现有一个背包,有一定的存储容量,现要求背包能存储物品的最大价值为多少,即背包中存储那些物品使得这些物品占有的价值最大,,物品不能重复
- 可以使用动态规划,动态规划可以使用填表法来实现,具体用程序完成,即使用二维数组,二维数组的行表示背包的容量,从0开始到最大,二维数组的列为物品,从零开始递增,一直到最后一件物品
- 先将二维数组的第一行第一列置为0,表示物品数为0,背包容量为0时背包的最大价值为0
- 用两个一维数组,一个记录物品,一个记录价格,两个数组应该有一一对应的关系
- 则开始填充二维数组,即表,开始遍历二维数组填充
- 如果二维数组当前位置:要放入的物品质量大于背包容量,则不能放入当前物品
- 如果要放入的物品质量小于当前位置的背包容量,则可以放入当前物品,而且还要有一定的剩余空间,再判断剩余的空间还能不能放入其他的物品,如果还能放入,则加入,如果不能就只能空着
- 上述思路执行完毕后,遍历二维数组则会看到背包物品放入的结果
- 源码见下
源码及分析
public static void main(String[] args) {
//物品质量
int[] w = {1, 4, 3};
//物品价值
int[] value = {1500, 3000, 2000};
//背包容量
int m = 4;
//物品个数
int n = w.length;
//创建二维数组类似一张表,使用填表法完成动态规划
int[][] v = new int[n + 1][m + 1];
int[][] path = new int[n + 1][m + 1];
//将二维数组的第一行第一列置为0
for (int i = 0; i < v.length; i++) {
v[i][0] = 0;
}
for (int i = 0; i < v[0].length; i++) {
v[0][i] = 0;
}
//使用动态规划向背包中添加物品
for (int i = 1; i < v.length; i++) {
for (int j = 1; j < v[0].length; j++) {
//如果背包的的容量小于物品的质量,则无法存放
if (j < w[i - 1]) {
v[i][j] = v[i - 1][j];
} else {
//如果背包的容量大于当前物品的质量,将当前物品放入背包后考虑剩余空间是否还可以存储一个物品
//v[i][j] = Math.max(v[i - 1][j], value[i - 1] + v[i - 1][j - w[i - 1]]);
if (value[i - 1] + v[i - 1][j - w[i - 1]] > v[i - 1][j]) {
v[i][j] = value[i - 1] + v[i - 1][j - w[i - 1]];
//如果还能存储一个物品,将当前位置置为1
path[i][j] = 1;
} else {
//否则就空着
v[i][j] = v[i - 1][j];
}
}
}
}
//查看背包的情况
for (int i = 0; i < v.length; i++) {
for (int j = 0; j < v[i].length; j++) {
System.out.print(v[i][j] + "\t");
}
System.out.println();
}
//输出将那些物品放入背包
//行和列的最大值
int i = path.length - 1;
int j = path[0].length - 1;
//循环结束的条件
while (i > 0 && j > 0) {
if (path[i][j] == 1) {
System.out.printf("第%d个商品放入背包\n", i);
//减去当前物品的重量
j -= w[i - 1];
}
i--;
}
}