01背包问题
01背包问题是一个很经典的动态规划算法题,给你一个固定容量的背包,然后你面前有一堆体积和价值不一的物品,你怎么挑选才能在不超过背包容量的情况下,物品的总价值尽可能高。
你可能一开始的想法是拿每个物品的价值除以体积,计算出每单位空间的价值是多少,然后进行排序,依次选取“性价比”高的物品。但是这种思路不一定能找到最优解,可能选完背包还剩一些空间,调整一下选取的物品可能会获得更大的价值,所以这里就要讲一下动态规划的解法。
思路
比如现在有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
问最多能装入背包的总价值是多大?
m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
我们思考的就是哪个物品可以选,哪个物品不选,现在用一个二维数组可以把这个过程描述的更加直观。
容量/物品 | 物品0 | 物品1 | 物品2 | 物品3 |
---|---|---|---|---|
容量1 | 0 | |||
容量2 | 1 | |||
容量3 | 1 | |||
容量4 | 1 | |||
容量5 | 1 | |||
容量6 | 1 | |||
容量7 | 1 | |||
容量8 | 1 | |||
容量9 | 1 | |||
容量10 | 1 |
当只有物品0时,各个容量的背包能装的物品总价值对多为1。然后当我们有物品1可选时:
容量/物品 | 物品0 | 物品1 | 物品2 | 物品3 |
---|---|---|---|---|
容量1 | 0 | 0 | ||
容量2 | 1 | 1 | ||
容量3 | 1 | 5 | ||
容量4 | 1 | 5 | ||
容量5 | 1 | 6 | ||
容量6 | 1 | 6 | ||
容量7 | 1 | 6 | ||
容量8 | 1 | 6 | ||
容量9 | 1 | 6 | ||
容量10 | 1 | 6 |
容量为3 和 4的背包可以选物品1,之后的背包容量够大,两个都要
现在我们再加入物品2供选择
容量/物品 | 物品0 | 物品1 | 物品2 | 物品3 |
---|---|---|---|---|
容量1 | 0 | 0 | 0 | |
容量2 | 1 | 1 | 1 | |
容量3 | 1 | 5 | 5 | |
容量4 | 1 | 5 | 5 | |
容量5 | 1 | 6 | 6 | |
容量6 | 1 | 6 | 6 | |
容量7 | 1 | 6 | 6 | |
容量8 | 1 | 6 | 7 | |
容量9 | 1 | 6 | 7 | |
容量10 | 1 | 6 | 8 |
在容量为8和9的时候我们把物品0、物品1组合替换为物品1、物品2组合,容量10够大,依旧是全都要!最后加入物品3供选择:
容量/物品 | 物品0 | 物品1 | 物品2 | 物品3 |
---|---|---|---|---|
容量1 | 0 | 0 | 0 | 0 |
容量2 | 1 | 1 | 1 | 1 |
容量3 | 1 | 5 | 5 | 5 |
容量4 | 1 | 5 | 5 | 5 |
容量5 | 1 | 6 | 6 | 6 |
容量6 | 1 | 6 | 6 | 6 |
容量7 | 1 | 6 | 6 | 6 |
容量8 | 1 | 6 | 7 | 7 |
容量9 | 1 | 6 | 7 | 7 |
容量10 | 1 | 6 | 8 | 9 |
最后容量10的时候,我们把物品0、物品1、物品2组合,替换成物品1、物品3组合,得到的最大价值。
那么代码如何实现呢?
public int backPackII(int m, int[] A, int[] V) {
int[][] dp = new int[m+1][A.length+1];
for (int i=0;i<A.length ;i++ ){
for (int u=1;u<=m;u++){
if(i>0){
dp[u][i]=u>=A[i] && dp[u-A[i]][i-1]+V[i]> dp[u][i-1]?dp[u-A[i]][i-1]+V[i]:dp[u][i-1];
}
else
dp[u][i]=u>=A[i]?V[i]:0;
}
}
return dp[m][A.length-1];
}
其实我们还可以再优化一下,用一个一维数组记录每个容量背包当下的最优解就行。
public int backPackII(int m, int[] A, int[] V) {
int[] dp = new int[m+1];
for (int i=0;i<A.length ;i++ ){
for (int u=m;u>=A[i];u--){
dp[u] = dp[u - A[i]]+V[i]>dp[u]?dp[u - A[i]]+V[i]:dp[u];
}
}
return dp[m];
}
如果有讲错的地方欢迎大家指正~有疑问的朋友也欢迎交流讨论