一.背包问题
有一个最大容量为C的背包,现有几个物品,每个物品都有对应的容量及价值,求在每个物品只能装一个且不超过容量的情况下,如何放置物品使背包中的价值最大
这个每个物品最多拿一个,属于01背包问题,每个物品无限拿属于无限背包问题.
二.思路讲解
用二维数组value来装在容量j和第i个物品情况下,能装入的'最大价值'
val[]表示每个物品的价值,w[]表示每个物体所占的容量
1.当j == 0或i == 0时,value[i][j]都为0,很好理解
2.当w[i]>j时,说明当前容量小于当前物品的容量,肯定放不进去,所以当前ij的最大价值肯定和上一个一样,value[i][j] = value[i-1][j]
3.当j>=w[i]时,容量装完当前物品后有所富余,所以得进行比较,是上一个i的最大价值大还是当前物品价值加上剩余容量价值比较大
value[i][j] = Math.max(value[i-1][j],val[i]+value[i-1][j-w[i]]);
三.上代码
class DynamicProgramming{
public static int[][] Knapsack(int[] val,int[] w,int C){
//val表示每个物品的单个价值
//w表示每个物品所占容量
//C表示背包总容量
//value表示第i个物品在j容量下的最大价值
int[][] value = new int[val.length+1][C+1];//考虑i == 0和j == 0,所以得加1
//循环从1开始,因为i==0和j==0,最大价值一定为0
for(int i = 1;i<value.length;i++){
for(int j = 1;j<C+1;j++){
if(w[i-1]>j){//w[i-1]是因为w数组第0个元素不是0,同理以下val数组也得-1
value[i][j] = value[i-1][j];//说明当前容量小于当前物品的容量
}else {
value[i][j] = Math.max(value[i-1][j],val[i-1]+value[i-1][j-w[i-1]]);
}
}
}
return value;
}
}
四.01背包优化
主要是讲空间复杂度降到O(n),压缩原先
思想和二维数组差不多,只是二维数组用的是上一行的数据,这次使用一维数组,需要滚动更新数据,
而且j得从后往前更,以放置一个物品多次放入
public static int[] Knapsack2(int[] val,int[] w,int C){
//val表示每个物品的单个价值
//w表示每个物品所占容量
//C表示背包总容量
//value表示第i个物品在j容量下的最大价值
int[] value = new int[C+1];//考虑i == 0和j == 0,所以得加1
//循环从1开始,因为i==0和j==0,最大价值一定为0
for(int i = 0;i<val.length;i++){
for(int j = value.length-1;j>=1;j--){
if(j<w[i]){
value[j] = value[j];
}else {
value[j] = Math.max(value[j],val[i]+value[j-w[i]]);
}
}
}
return value;
}
五.完全背包问题
上一个小标题说j逆序是防止物品重用,那么将j正序就能得到完全背包问题的解了
//完全背包问题二维数组形式
public static int[] Knapsack3(int[] val,int[] w,int C){
//val表示每个物品的单个价值
//w表示每个物品所占容量
//C表示背包总容量
//value表示第i个物品在j容量下的最大价值
int[] value = new int[C+1];//考虑i == 0和j == 0,所以得加1
//循环从1开始,因为i==0和j==0,最大价值一定为0
for(int i = 0;i<val.length;i++){
for(int j = value.length-1;j>=1;j--){
for (int k = 0;k<j/w[i]+1;k++){
value[j] = Math.max(value[j],k*val[i]+value[j-k*w[i]]);
}
}
}
return value;
}
//完全背包问题一维数组最终优化
public static int[] Knapsack4(int[] val,int[] w,int C){
//val表示每个物品的单个价值
//w表示每个物品所占容量
//C表示背包总容量
//value表示第i个物品在j容量下的最大价值
int[] value = new int[C+1];//考虑i == 0和j == 0,所以得加1
//循环从1开始,因为i==0和j==0,最大价值一定为0
for(int i = 0;i<val.length;i++){
for(int j = 0;j<value.length;j++){
if(j<w[i]){
value[j] = value[j];
}else {
value[j] = Math.max(value[j],val[i]+value[j-w[i]]);
}
}
}
return value;
}