分枝限界解决0/1背包问题

1、分枝限界法的概述
采用广度优先产生状态空间树的结点,并使用剪枝函数的方法称为分枝限界法。按照广度优先的原则,一个活结点一旦成为扩展节点后,算法将立刻将符合条件的孩子节点添加进入队列。
2、状态空间树生成过程的示例在这里插入图片描述
3、约束条件和规律
左孩子节点的UBB和父亲节点UBB一样不需要变动
当节点的层数等于物品个数且当前收益大于L时改变L的值,且记录节点ans的指针
当优先队列为空时结束进程返回L
进入右孩子节点,若L值小于LBB时更新L;
4、 结构
创建结构体Node,用来寻找解向量,只需要保存它的父亲节点就可以通过ans找到所有节点了,用布尔值left判断其是左孩子节点还是右孩子节点

struct Node{
 Node(Node* par,bool lft)
 {
  parent=par;
  left=lft;
 }
 Node* parent;
 bool left;
};

创建结构体pqNode,
保存背包剩余重量cu
已获得的收益profit
上界函数值UBB
当前节点层数level
状态空间树上相应的结点ptr

template<class T>
struct pqNode{
 pqNode(float cap,T prof,T ub,int lev,Node* p)
 {
  cu=cap;profit=prof;
  level=lev;
  UBB=ub;
  ptr=p;
 }
 float cu;
 T profit,UBB;
 int level;
 Node *ptr;
};

5、代码

#include <iostream>
#include<queue>
using namespace std;
struct Node{//状态空间树节点 
 Node(Node* par,bool lft)
 {
  parent=par;//父节点 
  left=lft;//若当前节点是左孩子,则left为true,否则为false 
 }
 Node* parent;
 bool left;
};
template<class T>
struct pqNode{//活结点结构 
 pqNode(float cap,T prof,T ub,int lev,Node* p)
 {
  cu=cap;profit=prof;
  level=lev;
  UBB=ub;
  ptr=p;
 }
 float cu;//背包的剩余载重 
 T profit,UBB;//以获收益profit和上界节点数值UBB 
 int level;//当前节点的level,根节点为0 
 Node *ptr;//解空间树上的相应节点 
};
template<class T>
bool operator<(pqNode<T> a,pqNode<T> b)//使优先队列按照按照UBB从大到小的顺序排序 
{
 return a.UBB < b.UBB;
}
template<class T>
class Knapsack{
 public:
  Knapsack(T* prof,float* wei,float mm,int len){//初始化事项 
   p=prof;
   w=wei;
   n=len;
   m=mm;
  }
  T LCBB();//求最优解值 
  void GenerateAns();//输出最优解搭配方案 
  private:
   void LUBound(T cp,float cw,int k,T&LBB,T& UBB);//计算节点的上下界UBB和LBB 
   T* p;//一维数组保存n个物品的收益 
   float* w,m;//一维数组保存物品的重量和背包承载重量 
   int n;//物品个数 
   Node* ans,*root;//指向状态空间树的根和最优解节点 
};
template<class T>
void Knapsack<T>::LUBound(T cp,float cw,int k,T&LBB,T&UBB)
{
 LBB=cp;//已获收益 
 float c=cw;//剩余载重 
 for(int i=k;i<n;i++)
 {
  if(c<w[i])
  {
   UBB=LBB+c*p[i]/w[i];//计算UBB:一般背包的最优解 
   for(int j=i+1;j<n;j++)//计算LBB:任意一个可行解 
   {
    if(c>=w[j])
     {
     c=c-w[j];
     LBB=LBB+p[j];
     }
   }
    return;
  }
  c=c-w[i];
  LBB=LBB+p[i];
 }
 UBB=LBB;//全部装入时,有UBB=LBB; 
}
template <class T>
T Knapsack<T>::LCBB()
{
 Node *child,*E;
 T LBB,UBB,L;
 ans=NULL;
 priority_queue<pqNode<T> > pq;//生成优先队列实列 
 root=new Node(NULL,false);//构造解空间树的根节点 
 E=root;
 LUBound(0,m,0,LBB,UBB);//计算UBB和LBB 
 pqNode<T> e(m,0,UBB,0,root);//跟结点成为检测根节点 
 L=LBB;//L初值为LBB 
 pq.push(e);
 do
 {
  int k;//记录层数 
  float cap;//记录//剩余容量 
  T prof;//记录所获价值 
  e=pq.top();//记录队头结点 
  pq.pop();//删除头节点 
  k=e.level;
  cap=e.cu;
  prof=e.profit;
  E=e.ptr;//记录空间树结点 
  if((k==n)&&(prof>=L)){
   L=prof;//修正L 
   ans=E; //记录答案结点 
  }
  else{
   if(cap>=w[k]){//生成左孩子结点 
    child=new Node(E,true);
    e.ptr=child;
    e.level=k+1;
    e.cu=cap-w[k];
    e.profit=prof+p[k];//e.UBB不变 
    pq.push(e);//加入结点 
   }
   LUBound(prof,cap,k+1,LBB,UBB);
   if(UBB>=L){//生成右孩子结点 
    child=new Node(E,false);
    e.ptr=child;
    e.level=k+1;
    e.cu=cap;
    e.profit=prof;
    e.UBB=UBB;
    pq.push(e);
    if(L<LBB)L=LBB;//修正L 
   }
  }
  if(pq.empty())
  { 
   	return L;//若为空返回L 
  } 
 }while(e.UBB>=L);//当e.UBB>=L继续循环 
 return L;
}
template<class T>
void Knapsack<T>::GenerateAns()
{
	cout<<"重量"<<" 价格"<<" 件数"<<endl;//输出搭配方案 

	for(int num=n-1;num>=0;num--)
	{
		if (ans->left)
		{
			cout<<w[num]<<"    "<<p[num]<<"    "<<1<<endl; 
		}
		else
		{
			cout<<w[num]<<"    "<<p[num]<<"    "<<0<<endl;
		}
			ans=ans->parent;
	}

}
int main(){
 float w[4]={2,4,6,9},p[4]={10,10,12,18};//w为重量数组,p为价值数组 
 Knapsack<float> G(p,w,15,4);//分别对应价值数组,重量数组,背包容量大小,物品数量 
 cout<<"最大价值为:"<<G.LCBB()<<endl;//输出最大价值 
 G.GenerateAns();//输出搭配方案 
}

6、运行结果

最大价值为:38
重量 价格 件数
9    18    1
6    12    0
4    10    1
2    10    1
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值