动态规划算法-背包问题
1.含义:
- 动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法
- 其与 分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
- 其与分治算法不同的是:动态规划算法经过分解得到的子问题的解不是相互独立的,后一个子问题的解要建立在前一个子问题解的基础上来进一步求解
2.背包问题
要想解决问题可通过填表的形式来直观体现出动态规划算法
-
可假定表中的行代表着背包的容量,列代表着要放入的第几个物品
-
表中的值就代表装入物品的价值
-
第一行和第一列设置为0代表着无背包时什么也装不下,有背包无物品时也什么都装不了。
-
公式
(1)v [ i] [0] = v[ 0] [j] = 0
(2)当weight[i] > j 时:v[i] [j] = v[i-1] [j]
(3)当j > weight[i] 时:
v[i] [j] = max{v[i-1] [j],value[i-1] + v [i-1] [j-weight[i-1]]}- 公式1含义就是将第一行第一列设为0
- 公式2含义就是当第i个商品的重量大于背包的容量时,背包的商品价值延用上一次装入的商品价值
- 公式3含义是当要装入的第i个商品重量小于背包的容量是,比较一个最大值,v [ i-1 ] [ j ]代表上一次装入的商品价值,v[i-1]代表前面i-1个商品,j-weight[i-1]代表装入i商品后此时剩余的容量,value[i-1]代表当前物品的价值
-
对应代码注释理解
public class KnapsackProblem {
public static void main(String[] args) {
//表示物品的重量
int[] weight = {1,4,3};
//表示对应物品的价值
int[] value = {1500,3000,2000};
//表示背包的容量
int m = 4;
//表示物品的个数
int n = value.length;
//创建二维数组
//v[i][j] 表示前i个物品中能装入背包容量为j的最大商品价值
int[][] v = new int[n+1][m+1];
//创建二维数组来存放放入的商品
int[][] path = new int[n+1][m+1];
//初始化第一列和第一行
for (int i = 0; i < v.length; i++) {
//给第一列初始化为0
v[i][0] = 0;
}
for (int i = 0; i < v[0].length; i++) {
//给第一行初始化为0
v[0][i] = 0;
}
//根据公式用动态规划处理
for (int i = 1; i < v.length; i++) {//不处理第一列
for (int j = 1; j < v[0].length; j++) {//不处理第一行
//如果当前物品的重量大背包重量,则v[i][j]继续沿用上一次的价值。
//为什么是weight[i-1]?因为i下标从1开始,而我们的weight下标从0开始
if (weight[i-1] > j) {
v[i][j] = v[i-1][j];
//如果大于等于
}else {
//对比出上一次的价值大还是要加入背包商品的价值加上前面前面i-1个商品的价值
//v[i-1]前面i-1个商品,j-weight[i-1]==》剩余的容量,当前物品的价值value[i-1]
//v[i][j] = Math.max(v[i-1][j],value[i-1] + v[i-1][j-weight[i-1]]);
//要打印哪个商品放入了背包得先用if else判
if (v[i-1][j] < value[i-1] + v[i-1][j-weight[i-1]]){
v[i][j] = Math.max(v[i-1][j],value[i-1] + v[i-1][j-weight[i-1]]);
//加入背包后将这个路径的二维数组对应的位置设为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[0].length; j++) {
System.out.print(v[i][j] + " ");
}
System.out.println();
}
/*for (int i = 0; i < v.length; i++) {
for (int j = 0; j < v[0].length; j++) {
System.out.print(path[i][j] + " ");
}
System.out.println();
}*/
//定义i ,j从path最后一个判断
int i = path.length-1;
int j = path[0].length-1;
while (i>0 && j>0) {
if (path[i][j] == 1) {
System.out.println("第"+ i + "个商品加入到背包");
//关键!如最后一个物品设为一肯定是将最后一个物品加入后能去到商品价值的最大值
//所以背包容量的j就要减去第i个商品的容量在再判断哪些物品被放进了背包
j = j - weight[i - 1];
}
i--;
}
}
}