回溯求解0-1背包

回溯法本质上是一种深度优先搜索状态空间树的算法。

 

如果不引入剪枝函数(约束函数+限界函数),则是穷举算法。

 

引入适当的限界函数,剪去已能确信不含最优答案结点的子树,使其成为一种启发式算法。

 

显示约束:xi=1表示将第i件物品装入背包, xi=0表示第i件物品不装入背包。
隐式约束:

 kk


解空间大小为2n 。解空间树为高度为n+1的满二叉树。

 

下界函数:变量L初值为0,遇到一个答案结点便计算该答案结点的收益值fp,且令L=max{L,fp}。则L中始终保存迄今为止已经搜索到的答案结点中收益的最大值,0/1背包的最优解值必定大于等于L,因此最优解值的下界估计值为L

 

限界函数:状态空间树中任一结点X,若其上界函数值bp<最优解值的下界估计值变量L,则可断定X子树上不含最优答案结点,可以剪去以X为根的子树。
剪去不含最优解的分枝【怎么都小于已经搜索到的答案节点收益最大值】

 

上界函数:当前位于状态空间树的结点X处,cw为背包当前重量,cp为当前已装入背包物品的总收益,则贪心法求解剩余载重和剩余物品构成的一般背包问题(物品编号k+1,k+2,...,n-1,载重M-cw)最大收益为rp则以X为根的子树上所有可能答案结点的目标函数值                     不可能超过bp=cp+rp,因此结点X的上界函数值为bp

 

8-4  设有0/1背包n=8, M=110(w0,w1,...,w7)=(1,11,21,23,33,43,45,55),
(p0,p1,...,p7)=(11,21,31,33,43,53,55,65)
——按pi/wi非增排列,即pi/wipi+1/wi+1


ffkk

lljj


kk


总结:向左走减去不可行的解的分支【不满足隐形约束的】 向右走减去不含最优解的【就是无论如何小于L的】


下面给出我的Java代码


public class Packet {
	
	public int []w;
	
	private int m;
	
	private int n;
	
	public int []p;
	
	public int fp;
	
	
	public Packet()
	{
		w=new int[]{30,10,20,50,40};
		p=new int[]{65,20,30,60,40};
	}
	public static void main(String args[])
	{
		Scanner input=new Scanner(System.in);
		Packet p=new Packet();
		int []x=new int [5];
		int []y=new int [5];
		p.m=100;
		p.n=5;
		p.BKnapsack(0, 0, 0, x, y);
		for(int i=0;i<5;i++)
		{
			System.out.println(x[i]);
		}
	}

	public int Bound(int k,int cp,int cw)
	{
		int b = cp, c = cw; // 当前收益cp和当前背包重量cw不变(xk=0)
		for (int i = k + 1; i < n; i++) // 对剩余物品求一般背包问题的解
		{
			c += w[i];
			if (c < m)
				b += p[i]; // 第i个物品可以全部放入
			else
				return b ;
		}
		return b;

	}
	
	
	public void BKnapsack(int k,int cp,int cw,int []x,int []y)
	{
		if (cw+w[k]<=m)	//左子树,需重新计算约束函数,上界函数无需计算
		   {
			y[k]=1;
			if (k<n-1) 		
			{
				BKnapsack(k+1,cp+p[k],cw+w[k],x,y);
			}
			if (cp+p[k]>fp && k==n-1)	//到最底层
			{	fp=cp+p[k];		//更新最优解下界估计值
				for (int j=0;j<=k;j++) x[j]=y[j];
			}
		   }
		 if (Bound(k,cp,cw)>=fp) //右子树,需重新计算上界函数,约束函数无需计算
		   {
			y[k]=0;
			if (k<n-1) 		//未到底
				BKnapsack(k+1,cp,cw,x,y);	
			if (cp>fp && k==n-1)	//到最底层
			{	fp=cp;		//更新最优解下界估计值
				for (int j=0;j<=k;j++) x[j]=y[j];
			}
		   }
	}


}


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值