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