动态规划_01背包
问题描述
01背包问题:假设你有一个背包,可以装4㎏东西,但是你有很多东西比如;锤子(1000元,1kg),棒子(1500元,2kg),棍子(2000元,1kg)…等。你要怎么样在你的书包里放入组合起来价值最大的物品。简而言之,在有限的空间怎样搭配才可以装入最大价值的东西,并且物品不重复。
解决思路
穷举法:把所有的可能性都列举出来,然后一一对比,可以达到一个解决问题的目的,但是可选择的物品如果多一些,从时间上和效率上,并不可取。
动态规划:怎么去理解动态规划呢。就是从最优解的基础上去推出下一个最优解。
原理
动态规划与分治算法类似,都是把大问题拆分成小问题,但是不同于分治算法的是,动态规划拆分后的每个小问题的解都取决与上一个问题的解,动态规划里的每个结果都是互相依赖的,最后的结果也是一层一层的推出来的,就好像是填表,一张结果有序的表,是不是得从上往下或从左到右依次计算然后填上结果,为什么呢,因为计算出本次的值才方便计算下一个值,动态规划也是一样的思想。在动态规划里表的最后一个值就是结果。
分析
物品的重量罗列出来,放入w数组中
int[ ] w = {…}
物品价值也罗列出来,放入val数组
int[ ] val = {…}
背包的容量保存下来,记作m
int m = 4;
物品个数也保存下来,记作n
int n = val.length;
创建二维数组保存每次放入的结果。
int [ ] [ ] v = new int [n + 1] [m + 1]
往背包里放入物品的话有两种情况
第一,包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即 v [ i ] [ j ] = v[ i - 1 ] [ j ]
第二,还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即 v [ i ] [ j ] = Math.max(v [i - 1] [ j ] , val [ i - 1] + v[ i - 1] [ j - w[ i - 1] ])
得出两个递推关系式:(比较重要重点理解)
如果 w[ i ] > j
v [ i ] [ j ] = v[ i - 1 ] [ j ]
如果w[ i ] < j
v [ i ] [ j ] = Math.max(v [i - 1] [ j ] , val [ i - 1] + v[ i - 1] [ j - w[ i - 1] ])
然后就可以根据这两个公式一个一个的填表
代码实习
public class KnapsackProblem {
/**因为递推可以充分利用前面保存的子问题的解来减少
*重复计算,所以对于大规模问题来说有递归不可比拟的优势,
*这也是动态规划算法的核心之处。
*由于动态规划解决问题有重叠问题这个特点
* 为了保证减少计算 每个子问题只计算一次
* 将其不同阶段的不同状态保存到一个二维数组
* 动态规划不同于分治的是 适用于动态规划规则
* 求解的问题经分解后的每一个子问题往往不是互相独立的
* (下一个子阶段的求解是建立在上一个阶段求解的基础上
* 进一步求解)
*/
public static void main(String[] args) {
//物品得重量
int[] w = {1, 4, 3};
//物品得价值
int[] val = {1500, 3000, 2000};
// 背包得容量
int m = 4;
//物品得个数
int n = val.length;
// 创建二维数组
// v[i][j] 表示前i个物品中能够装入容量为j得背包
//中得最大价值
int[][] v = new int[n + 1][m + 1];
// 初始化第一行和第一列
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;
}
// 为了记录存储记录 定义一个数组
int[][] path = new int[n+1][m+1];
// 根据公式取动态处理背包问题
//不处理第一行
for (int i = 1; i < v.length; i++) {
// 如果当前要存商品的重量大于
//容量不处理第一列
for (int j = 1; j < v[0].length; j++) {
// 因为从1开始 所有w-1
if (w[i - 1] > j) {
// 我们就采取上一层的方案
v[i][j] = v[i - 1][j] ;
} else {
/*// 如果要存商品的重量小于或等于
//当前背包容量
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] = v[i - 1][j];
}
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;
}
}
}
}
for (int i = 0; i < v.length; i++) {
for (int j = 0; j < v[i].length; j++) {
System.out.print(v[i][j] + " ");
}
System.out.println();
}
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--;
}
}
}
总结
动态规划核心思想就是用上一个最优结果推出下一个最优结果,就减少了很多的重复计算,每次得到的结果都是建立在上一个最优解上,所以每次得到的结果也是最优解,进而可以推出下一次的最优解,这就是动态规划很精妙的地方。
秃头萌新一枚 希望可以帮助到大家理解