分支限界、回溯法解决01背包问题

分支限界法

分支限界搜索,以广度优先或最小耗费优先的方式搜索解空间。
这里直接是用的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);
		}
	}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值