0 背包问题
有一个背包,容量为4磅,现有如下物品
物品 | 重量 | 价格 |
---|---|---|
吉他(G) | 1 | 1500 |
音响(S) | 4 | 3000 |
电脑(L) | 3 | 2000 |
1)要求达到的目标为装入的背包的总价值最大,并且重量不超出
2)要求装入的物品不能重复(01背包)
1 动态规划
- 动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法
- 动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
- 与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。 ( 即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解 )
- 动态规划可以通过填表的方式来逐步推进,得到最优解.
- 开始填表,对于给定的 n 个物品,设 v[i]、w[i]分别为第 i 个物品的价值和重量,C 为背包的容量。再令 v[i] [j]表示在前 i 个物品中能够装入容量为 j 的背包中的最大价值。则我们有下面的结果:
- 填表如下:
物品i\容量j | 0磅 | 1磅 | 2磅 | 3磅 | 4磅 |
---|---|---|---|---|---|
空气 | 0 | 0 | 0 | 0 | 0 |
吉他(G) | 0 | 1500(G) | 1500(G) | 1500(G) | 1500(G) |
音响(S) | 0 | 1500(G) | 1500(G) | 1500(G) | 3000(S) |
电脑(L) | 0 | 1500(G) | 1500(G) | 2000(L) | 2000(L)+1500(G) |
- 代码最终实现表格,而容量j=4时,背包中可放入第三种物品(电脑)时,表格的末尾就是背包问题的答案,思考:那么怎么自动通过已有的表格,动态获取问题的答案呢?(你总得知道背包里放了什么吧)
//动态规划:背包问题
public class Knapsack {
public static void main(String[] args) {
int[] w = {1, 4, 3};
int[] val = {1500, 3000, 2000};
int m = 3+1;
int n = 4+1;
int[][] v = new int[m][n];
int[][] path = new int[m][n];
//填表
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
//考虑到v数组的0容量和无物品可放的情况,所以使用w和val数组时需要索引-1
if (w[i - 1] > j) {
v[i][j] = v[i - 1][j];
} else {
//v[i-1][j-w[i-1]] 含义:j-w[i-1]表示可用容量,而第一个i-1表示第i个物品已被使用,所以i-1
//v[i][j] = Math.max(v[i - 1][j],val[i-1]+v[i-1][j-w[i-1]]);
if (v[i - 1][j] < val[i - 1] + v[i - 1][j - w[i - 1]]) {
v[i][j] = val[i-1]+v[i-1][j-w[i-1]];
path[i][j] = 1;
} else {
v[i][j] = v[i - 1][j];
}
}
}
}
//表示从表的最后一列从下往上查看是否放入
int p = m-1;
int q = n-1;
while (p > 0 && q > 0) {
if (path[p][q] == 1) {
System.out.println("放入第"+p+"个物品");
//放入一件则扣除容量
q -= w[p - 1];
}
//寻找可放入的物品
p--;
}
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();
}
}
}