什么是动态规划算法?
-
动态规划算法(Dynamic Programming),也叫dp算法,该算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解;动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
-
dp与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。 ( 即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解 ),依次解决各子问题,最后一个子问题就是初始问题的解,而分治法各个子问题是相互独立的。
-
动态规划算法的典型解题套路就是创建一个二维数组,然后通过填表的方式逐步推导出解决问题的递推公式。
动态规划算法的典型应用:
背包问题(01背包)
01背包和完全背包的区别:
-
01 背包指的是:每个物品最多放一个
-
完全背包指的是:每种物品都有无限件可用,完全背包可以转化为 01 背包
背包问题:有一个背包,容量为 4 磅 , 现有如下物品
- 要求达到的目标为装入的背包的总价值最大,并且重量不超出
- 要求装入的物品不能重复
思路分析:
设 val[i]、w[i]分别为第 i 个物品的价值和重量
v [i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值
//将表第一行和第一列置为0,将物品多增一行和背包的容量多增一列是为了防止后面的公式出现数组越界异常
v[i][0]=v[0][j]=0;
// 当准备加入新增的商品的容量大于 当前背包的容量时,就直接使用上一个单元格的装入策略
当 w[i]> j 时:v[i][j]=v[i-1][j]
//当准备加入的新增的商品的容量小于等于当前背包的容量
当 j>=w[i]时: v[i][j]=max{v[i-1][j], val[i]+v[i-1][j-w[i]]}
v[i-1][j]表示上一行对应列的单元格的值,也就是没有新增当前商品之前对应重量的最大价值
val[i-1]表示当前商品的价值,注意下标也是要减一
j-w[i-1]表示当前容量减去当前物品的容量后剩余容量
v[i-1][j-w[i-1]]表示没有加入当前商品前剩余这个容量对应的最大价值
图解:
代码实现:
public class knapsackProblem {
public static void main(String[] args) {
int[] w={1,4,3};//物品的重量,分别表示吉他1磅,音响4磅,电脑3磅
int[] val={1500,3000,2000};//物品的价值,分别表示吉他1500,音响4000,电脑3000
int m=4;//背包的容量
int n=val.length;//物品的个数
//创建一个二维数组
int[][] v=new int[n+1][m+1];//将物品多增一行和背包的容量多增一列是为了防止后面的公式出现数组越界异常
//将二维数组的第一行和第一列初始化为0;其实也可以不用手动初始化的,因为数组默认初始值就是0
for(int i=0;i<v.length;i++){
v[i][0]=0;
}
for(int j=0;j<v[0].length;j++){
v[0][j]=0;
}
//填表,注意是从下标为1的那一行以及下标为1的那一列开始填的
for(int i=1;i<v.length;i++){
for(int j=1;j<v[0].length;j++){
//因为下标是从1开始,所以w数组的下标要减一
if(j<w[i-1]){
//将上一行对应列的单元格的值赋给当前位置
v[i][j]=v[i-1][j];
}else{
//j>=w[i-1]的情况
//v[i-1][j]表示上一行对应列的单元格的值,也就是没有新增当前商品之前对应重量的最大价值
//val[i-1]表示当前商品的价值,注意下标也是要减一
//j-w[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]]);
}
}
}
//输出填写的二维数组
for(int i=0;i<v.length;i++){
for(int j=0;j<v[0].length;j++){
System.out.print(v[i][j]+"\t");
}
System.out.println();
}
System.out.println("能装入的最大价值为:"+v[v.length-1][v[0].length-1]);
}
}