动态规划之背包问题,拿下!
“是一个经典的动态规划题目。
1. 问题描述
现在有四个物品,小偷的背包总容量是8,怎么样偷得物品是最大价值的?
假设现在有4种物品:
物品编号 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
物品重量 w | 2 | 3 | 4 | 5 |
物品价值 v | 3 | 4 | 5 | 8 |
2. 问题分析
假设函数 f(k,w):当背包容量为w,现有k件物品可以偷,所能偷到的最大价值。
以 f(4,8) 为例,也就是说4件物品,8的容量
由上面可以得到最大价值为12。
而且我们还可以知道状态转移方程应该怎么列。
如果我们获得了最大价值的情况,我们怎么知道背包装了什么物品呢?回溯
主要是基于状态转移方程来逆推,比如我们最后得到的f(4,8)=12,如果4号物品没有被偷,那么他应该和f(3,8)相等,但是,这里根据表格可以看到,两者不等,说明4号物品已经确认偷了;偷了4号物品,背包容量就剩下3,这是我们就要看f(3,3)了,判断第3个物品有没有被偷,方法一样,他跟f(2,3)是一样的,说明没有被偷....
3. 代码解法
public class KnapsackProblem {
public static void main(String[] args) {
// 这是一个二维数组,用来存储当前物品及重量下的最大价值
int[][] f = new int[5][9];
// 5个物品 对应的重量以及价值
int[] w = {0,2,3,4,5};
int[] v = {0,3,4,5,8};
// 根据状态转移方程来填写前面声明的二维数组,自底向上
for (int i = 1; i < 5; i++) {
for (int j = 1; j < 9; j++) {
// i为容量,j为价值
// 当前物品的重量太大,装不下
if (w[i]>j){
f[i][j] = f[i-1][j];
}else{
// 可以装得下,装还是不装,哪个能获得的价值最大
f[i][j] = Math.max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
}
}
}
// 输出4个物品和8个容量的最大价值
System.out.println(f[4][8]);
}
// 回溯自己想
}
如何回溯呢?
public class KnapsackProblem {
//int[] w = {1,4,3};
//int[] val = {1500,3000,2000};
static int[][] f = new int[5][9];
static int[] w = {0,2,3,4,5};
static int[] v = {0,3,4,5,8};
static int[] object = new int[5];
static void Dynamic(){
//int i,j;
for (int i = 1; i < 5; i++) {
for (int j = 1; j < 9; j++) {
if (w[i]>j){
f[i][j] = f[i-1][j];
}else{
f[i][j] = Math.max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
}
}
}
System.out.println(f[4][8]);
}
static void Find(int i,int j){
// 说明回溯结束或者一开始就是0
if (i == 0){
System.out.println("装包物品为:");
for (int i1 = 0; i1 < 5; i1++) System.out.print(object[i1]+" ");
return;
}
// 如果没有偷
if (f[i][j] == f[i-1][j]){
object[i] = 0;
Find(i-1,j); // 递归
// 如果偷了当前的
}else if (f[i][j] == f[i-1][j-w[i]]+v[i]){
object[i] = i;
Find(i-1,j-w[i]);
}
}
public static void main(String[] args) {
Dynamic();
Find(4,8); // 偷的是2,4号物品
}
}
- END -