动态规划算法介绍:
1.动态规划(Dynamic Programming)算法的核心思想是将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法
2.动态规划算法和分治算法类似,基本思想也是将待求解的问题分解成若干个子问题,先求解子问题,然后从子问题中求解出原问题的解
3.和分治算法不同的是,用动态规划求解的问题,分解的子问题往往不是独立的(下一个阶段的求解是建立在上一个阶段的基础上)
4.动态规划可以通过填表的方式逐步进行推进,得到最优解
动态规划的经典问题:01背包问题
要求:
达到的目标为装入背包的总价值最大,但是重量不得超出背包容量,装入的物品不得重复
介绍:
背包问题主要是指一个给定容量的背包,若干具有一定价值和重量的物品。如何选择物品放入背包使得物品的价值最大。其中又分为01背包问题和完全背包问题,01背包问题就是装入的物品不能重复,完全背包问题就是装入的物品可以重复 完全背包问题可以转换为01背包问题
思路分析:
利用动态规划解决,每次遍历到第i个物品,根据w[i]和val[i]来表示物品的重量和价值,设置C为背包的容量,再令v[i][j]表示前i个物品中能够装入容量为j的背包的最大价值
公式总结(按照表格更容易理解一点):
(1): v[i]0]=v[0][j]=0; 表示填入表的第一行和第一列为0
(2): w[i]>j v[i][j]=v[i-1][j] 当新增加的商品重量大于背包容量时,直接拿取上一层数据即可(未加该物品时的最大价值)
(3): j>=w[i]时,v[i][j]=mx{v[i-1][j],val[i]+v[i-1][j-w[i-1]]}
分析:
当准备加入的新增商品容量小于或者等于当前背包容量(就是说明新增的物品可以往里面装),开始比较价值
v[i-1][j]:填充的上一层数据(未加该物品时的最大价值)
val[i]:当前商品价值
v[i-1][j-w[i]] 在装入了当前商品后,v[i-1][j-w[i-1]]表示i-1个商品装入j-w[i]空间时产生的价值
分析验证
假设以v[3][4]为例
i=3,j=4
w[i-1]=w[2]=3 (待装入的物品:电脑) 注意:这里的i和前面的i性质并不一样
j=4>w[2] v[3][4]=max{v[2][4],val[2]+v[2][4-3]}=max{2000,2000+1500}=3500
验证完毕
代码编写:
注:代码的结构主要分为三部分,第一部分就是初始化,第二部分是根据公式确定背包问题摆放(填表),第三部分就是展示结果
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];
//为了记录商品放入的情况,我们定一个二维数组
int[][] path = new int[n + 1][m + 1];
2.根据公式迭代处理(注意w[i]和val[i]要变成 w[i-1]和val[i-1](由于for循环是由1开始)【对公式进行一些变化】)
System.out.println("==================");
//初始化第一行第一列,不去处理默认也为0
for (int i = 0; i < v.length; i++) {
v[i][0] = 0;//第一列为0
}
for (int i = 0; i < v[0].length; i++) {
v[0][i] = 0;//第一行设置为0
}
//根据公式进行迭代处理
for (int i = 1; i < v.length; i++) {//不处理第一行 i从1开始
for (int j = 1; j < v[0].length; j++) { //不处理第一列 j从1开始
//因为我们的程序i是从1开始的 因此原来公式中的w[i] 修改为w[i-1]
if (w[i - 1] > j) {
v[i][j] = v[i - 1][j];
} else {
//因为我们的i从1开始,因此公式要调整成
// v[i][j]=Math.max(v[i-1][j],val[i-1]+v[i-1][j-w[i-1]]);
// v[i][j] = Math.max(v[i - 1][j], val[i - 1] + v[i - 1][j - w[i - 1]]);
//为了记录商品存放的情况 采用if else替代处理
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
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] + " ");
}
System.out.println();
}
3.打印最终结论(逆序打印最终结果即可)
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--;
}
}
运行结果: