1 题目
一个背包的总容量为V
,现在有N
类物品,第i
类物品的重量为weight[i]
,价值为value[i]
。
那么往该背包里装东西,怎样装才能使得最终包内物品的总价值最大。
2 解题思路
本题目的解题思路类似金矿问题,放到背包里面的物体要么整个放进去,要么就不放。
那么根据动态规划的思路就有下一下两种情况:
- 放进背包:
此时将物体i
放进背包,那么所剩物体减少1个,背包所能继续装物体的重量为V-weight[i]
,背包总价值加上物体i
的价值。 - 不放:
此时不放到背包中,所剩物体减少1个,背包所能继续装物体的重量不变,背包的总价值维持不变。
这里假设函数f(n,w)
是计算背包最大价值的函数,那么状态转移方程如下:
- 放进背包:
f(n,w)=max(f(n-1, w-weight[i]), f(n-1,w))
- 不放进背包:
f(n,w)=f(n-1,w)
那么具体的实现有以下三种:
(1)递归实现
/**
*
* @param n 物体的数量
* @param w 背包所能放物体的最大重量
* @param wt 每件物体的重量
* @param val 每件物体的价值
* @return
*/
public int recursion(int n, int w, int[] wt, int[] val) {
if (w == 0 || n == 0)
return 0;
if (wt[n - 1] < w) //放
return Math.max(recursion(n - 1, w, wt, val), recursion(n - 1, w - wt[n - 1], wt, val) + val[n - 1]);
else
return recursion(n - 1, w, wt, val);
}
(2)二维数组实现的动态规划
/**
*
* @param n 物体的数量
* @param w 背包所能放物体的最大重量
* @param wt 每件物体的重量
* @param val 每件物体的价值
* @return
*/
public int dpSolution(int n, int w, int[] wt, int[] val) {
int[][] res = new int[n + 1][w + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= w; j++) {
if (j >= wt[i - 1])//背包所能承受的重量大于物体的重量,就放进去
res[i][j] = Math.max(res[i - 1][j], res[i - 1][j - wt[i - 1]] + val[i - 1]);
else
res[i][j] = res[i -1][j];
}
}
return res[n][w];
}
(3)一维数组实现的动态规划
/**
*
* @param n 物体的数量
* @param w 背包所能放物体的最大重量
* @param wt 每件物体的重量
* @param val 每件物体的价值
* @return
*/
public int dpSolution2(int n, int w, int[] wt, int[] val){
int [] dp=new int[w+1];
for (int i = 1; i < n+1; i++) {
for (int j=w;j>0;j--) {
if (j>=wt[i-1])
dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
}
}
return dp[w];
}
以上三种写法的算法复杂度可以参考金矿问题。
3 总代码
package DP;
import java.util.Arrays;
/**
* 背包问题:
* 给你一个可装载重量为 W 的背包和 N 个物品,每个物品有重量和价值两个属性。
* 其中第 i 个物品的重量为 wt[i],价值为 val[i],现在让你用这个背包装物品,
* 最多能装的价值是多少?
*/
public class BackpagQuestion {
/**
*
* @param n 物体的数量
* @param w 背包所能放物体的最大重量
* @param wt 每件物体的重量
* @param val 每件物体的价值
* @return
*/
public int recursion(int n, int w, int[] wt, int[] val) {
if (w == 0 || n == 0)
return 0;
if (wt[n - 1] < w) //放
return Math.max(recursion(n - 1, w, wt, val), recursion(n - 1, w - wt[n - 1], wt, val) + val[n - 1]);
else
return recursion(n - 1, w, wt, val);
}
/**
*
* @param n 物体的数量
* @param w 背包所能放物体的最大重量
* @param wt 每件物体的重量
* @param val 每件物体的价值
* @return
*/
public int dpSolution(int n, int w, int[] wt, int[] val) {
int[][] res = new int[n + 1][w + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= w; j++) {
if (j >= wt[i - 1])//背包所能承受的重量大于物体的重量,就放进去
res[i][j] = Math.max(res[i - 1][j], res[i - 1][j - wt[i - 1]] + val[i - 1]);
else
res[i][j] = res[i -1][j];
}
}
return res[n][w];
}
/**
*
* @param n 物体的数量
* @param w 背包所能放物体的最大重量
* @param wt 每件物体的重量
* @param val 每件物体的价值
* @return
*/
public int dpSolution2(int n, int w, int[] wt, int[] val){
int [] dp=new int[w+1];
for (int i = 1; i < n+1; i++) {
for (int j=w;j>0;j--) {
if (j>=wt[i-1])
dp[j] = Math.max(dp[j],dp[j-wt[i-1]]+val[i-1]);
}
}
return dp[w];
}
public static void main(String[] args) {
int n = 3;
int w = 4;
int[] wt = {2, 1, 3};
int[] val = {4, 2, 3};
BackpagQuestion backpagQuestion = new BackpagQuestion();
int res = backpagQuestion.recursion(n, w, wt, val);
System.out.println("递归求解:" + res);
System.out.println("dp求解:" + backpagQuestion.dpSolution(n, w, wt, val));
System.out.println("第二种解法:"+backpagQuestion.dpSolution2(n,w,wt,val));
}
}