分支限界法
分支限界搜索,以广度优先或最小耗费优先的方式搜索解空间。
这里直接是用的FIFO。
其选择下一扩展结点的策略是:在每一个活结点处,计算一个函数值(限界),并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。
针对01背包问题,需要确定一个上界函数,见代码。
对于每一个非叶活结点,其左孩子表示装入下一个物品,则需要判断是否会超出背包容量;对于其右孩子(不装入下一个物品)来说,一定不会超出背包容量,但是可以对其做约束来剪枝,仅当往下发展会有更大上界时才将其加入。
源代码:
public FindMax3(){
Scanner sc=new Scanner(System.in);
//物品数量
System.out.println("please input the number of the things:");
num = sc.nextInt();
weight = new float[num+1];
value = new float[num+1];
//背包容量
System.out.println("please input the limit of the weight:");
limit = sc.nextInt();
System.out.println("请输入每个物品的重量、价值:");
for(int i=0;i<=num;i++){
weight[i] = sc.nextFloat();
value[i] = sc.nextFloat();
}
queue = new LinkedList<HeapNode>();
//初始化
cur_value = 0;
cur_weight = 0;
best = 0;
}
//求上界
public float maxBound(int t){
//剩余容量
float left = limit-cur_weight;
//价值上界
float bound = cur_value;
while((t<num) && (weight[t]<=left)){
left -= weight[t];
bound += value[t];
t++;
}
//剩余容量已经放不下第t个物品了,计算上界
if(t<num){
bound += (value[t]/weight[t])*left;
}
return bound;
}
//加入可行结点
private void addLiveNode(float upper,float curvalue,float curweight,int level){
HeapNode node = new HeapNode();
node.upBound = upper;
node.value = curvalue;
node.weight = curweight;
node.level = level;
if(level<=num){
queue.offer(node);
}
}
private float knapsack(){
int i=0;
float upbound = maxBound(i);
while(true){
//判断左节点的可行性,可行就加入队列
float wt = cur_weight + weight[i];
if(wt <= limit){
if(cur_value + value[i] > best){
best = cur_value + value[i];
}
addLiveNode(upbound, cur_value+value[i], cur_weight+weight[i], i+1);
}
//右节点一定是不超过limit的,对它做约束,仅当往下发展有可能有更大上界的时候才加入
upbound = maxBound(i+1);
if(upbound >= best){
addLiveNode(upbound, cur_value, cur_weight, i+1);
}
System.out.println(queue.size());
if(queue.isEmpty()){
return best;
}
HeapNode node = queue.peek();
queue.poll();
cur_value = node.value;
cur_weight = node.weight;
upbound = node.upBound;
i = node.level;
}
}
回溯法
回溯法其实思想跟分支限界很像,选择下一个扩展结点的策略也基本相同,但是回溯法是深度优先搜索的,这里用的递归版。回溯法可以输出所有符合条件的解,而分支限界求解目标是最优解。
public void knapsack(int k){
if(k>num){
best_val = current_val;
for(int i=1;i<=num;i++){
if(1==choosed[i]){
System.out.println("选中第"+i+"个物品");
}
}
System.out.println("该方案下装入的价值为:"+best_val);
return;
}
//左节点是否可行
if(current_wei+weight[k]<=limit){
current_val += value[k];
current_wei += weight[k];
choosed[k]=1;
knapsack(k+1);
current_val -= value[k];//回溯
current_wei -= weight[k];//回溯
choosed[k]=0;
}
//右节点可能有更大的上界才加入
if(maxBound(k)>best_val){
knapsack(k+1);
}
}