背包问题涉及的是子集树,层数即物品的数量,
每个物品有选和不选两种选择,即每个节点有左子树(选)和右子树(不选)
而恰当的剪枝函数体现在对待右子树上,
因为右子树至少是不选1个了,因此很有可能出现以下情况:
剩下的所有物品价值加起来还没有目前为止的maxvalue大
当这种情况出现时,就可以剪枝了,避免不必要的查找。
package niuke;
/** * Created by asus on 2017/11/29. */
public class Bags01 {
int n = 5;
int capacity = 10;
int[] weight = {2,6,4,1,5};
double[] value = {6,9,6,1,4};
int maxValue = 0;
int tempValue;
int tempWeight;
int[] way = new int[n];
int[] bestWay = new int[n];
public void backTrack(int t){
//已经搜索到根节点
if(t>n-1){
if(tempValue > maxValue){
maxValue = tempValue;
for(int i=0;i<n;i++)
bestWay[i] = way[i];
}
return;
}
//搜索左边节点
if(tempWeight + weight[t] <= capacity){
tempWeight += weight[t];
tempValue += value[t];
way[t] = 1;
backTrack(t+1);
tempWeight -= weight[t];
tempValue -= value[t];
way[t] = 0;
}
//不装入这个物品,直接搜索右边的节点
if( bound(t+1) >= maxValue){
backTrack(t+1);
}
}
//用于计算剩余物品的最高价值上界
public double bound(int k){
double maxLeft = tempValue;
int leftWeight = capacity - tempWeight;
//尽力依照单位重量价值次序装剩余的物品
while(k <= n-1 && leftWeight > weight[k] ){
leftWeight -= weight[k];
maxLeft += value[k];
k++;
}
//不能装时,即leftWeight < weight[k],但此时并不能确保该物品之后就没有可选的了
// 用下一个物品的单位重量价值折算到剩余空间作为推测。
if( k <= n-1){
maxLeft += value[k]/weight[k]*leftWeight;
}
return maxLeft;
}
public static void main(String[] args){
Bags01 b = new Bags01();
b.backTrack(0);
System.out.println("该背包能够取到的最大价值为:"+b.maxValue);
System.out.println("取出的方法为:");
for(int i : b.bestWay)
System.out.print(i+" ");
}
}
//output
/* 该背包能够取到的最大价值为:16 取出的方法为: 1 1 0 1 0 */