一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
1、介绍
动态规划算法介绍
动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法。
动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若千个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,==经分解得到子问题往往不是互相独立的==。(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。
动态规划可以通过填表的方式来逐步推进,得到最优解。
2、01背包问题
01背包:每个物品最多放一个
完全背包:每种物品都有无限件使用
背包问题
有一个背包:容量为4磅,现有如下物品
| 物品 | 重量/磅 | 价格 |
|---|---|---|
| 吉他(G) | 1 | 1500 |
| 音响(S) | 4 | 3000 |
| 电脑(L) | 3 | 2000 |
要求:
- 装入背包的总价值最大,并且重量不超出
- 装入的物品不能重复
思路
利用动态规划来解决。每次遍历得到第i个物品,根据w[i] 和 val[i] 来确定是否需要将该物品放入背包中。即对于给定的n个物品,设val[i] 、w[i]分别为第i个物品的价值和重量,C为背包容量。再令v[i][j]表示背包容量为j时,装入了i个物品时的最大价值。
可得推导出下面公式:
v[i][0]=v[0][j]=0- 当w[i] > j 时:
v[i][j]=v[i-1][j] - 当 w[i] <= j时:
v[i][j]= Math.max(v[i - 1][j], val[i] +v[i][j - w[i]])
| 物品及价值/背包容量 | 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) |
上表中 行 表示物品及价值,列 表示背包容量:
-
val[0] = 0,val[1] = 1500,val[2] = 3000,val[3] = 2000
-
w[0] = 0, w[1] = 1500,w[2] = 4, w[3] = 3
-
当背包容量使用了4磅,放入一个电脑和一个吉他时,价值最大,此时
v[i][j]=v[3][4]= val[3] +v[3][4-w[3]]= 2000 +v[3][1]= 3500
代码实现
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]表示最大价值,表示背包容量为j时,装入了i个物品时的最大价值
int[][] v = new int[n+1][m+1];
//创建二维数组,为了记录商品的放入情况
int[][] path = new int[n+1][m+1];
//初始化第一行第一列(默认为0)
for (int i = 0; i < n; i++) {
v[i][0] = 0;//初始化第一列为0
}
for (int i = 0; i < m; i++) {
v[0][i] = 0;//初始化第一行为0
}
//根据公式来动态规划处理
for (int i = 1; i < v.length; i++) {
for (int j = 1; j < v[0].length; j++) {
if (w[i-1]>j){
v[i][j] = v[i-1][j];
}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[i][j] = 1;//记录当前情况,符合要求
}else {
v[i][j] = v[i - 1][j];
}
}
}
}
//从path的最后开始遍历
int i = path.length - 1;
int j = path[0].length - 1;
int count = 0;
while(i > 0 && j > 0){
if (path[i][j] == 1){
System.out.printf("第%d个商品放入背包\n" ,i);
j-=w[i-1];
count+=val[i-1];
}
i--;
}
System.out.println("背包产生最大价值为:"+count);
}
}
执行结果:
第3个商品放入背包
第1个商品放入背包
背包产生最大价值为:3500