优先队列式分支限界法0-1背包

 
#include<iostream>   
#include <algorithm>   
#include "heap.h"
using namespace std;   

class Knap   
{   
public:   
    Knap(double *pp,double *ww,double cc,int nn)//构造函数   
    {   
        p=pp;   
        w=ww;   
        c=cc;   
        n=nn;   
        cw=0;   
        cp=0;   
		E=0;  
        bestx=new int[n+1]; 
		H=init(100); 
    }   
    double knapsack();//找最优值的函数。   
    double Bound(int i);//边界函数   
	void AddLiveNode(double up,double cp,double cw,bool ch,int lev);
	int MaxKnapsack();

    void output()//输出最佳路径   
    {   
        for(int i=1;i<=n;i++)   
            cout<<bestx[i]<<" ";   
        cout<<endl;   
    }   
 
private:   
    double c;   //背包容量
    int n;      //物品总数
    double *w;   //物品重量数组
    double *p;   //物品价值数组
    double cw;   //当前背包重量
    double cp;   //当前背包价值
    bbnode *E;   //指向扩展结点的指针
    int *bestx;   //最优解结构
	HeapQueue H;
  
};   

class Object   
{   
public:   
    int ID;   
    double d;   
};   
  
int cmp(Object a,Object b)//定于排序类型。   
{   
    return a.d>b.d;//降序   
}   
  
double Knap::Bound(int i)   
{   
  
    Object *Q=new Object[n+1];   
    int j;   
    for(j=1;j<=n;j++)   
    {   
        Q[j].ID=j;   
        Q[j].d=1.0*p[j]/w[j];   
    }   
    sort(Q+1,Q+n+1,cmp);/*对数组Q排序。按cmp()方式排序。这里要注意是从Q[1]开始而不是从Q开始*/  
	/*这里的Object 数组其实可以只创建一次就可以了,像现在这样每次调用Bound()都要创建并且对其进行排序,浪费资源,有待改进*/

/*
    for(j=1;j<=n;j++)  
        cout<<Q[j].ID<<" ";  
    cout<<endl;  
*/
  
    double cleft=c-cw;   
    double b=cp;   
    while(i<=n&&w[Q[i].ID]<=cleft)   
    {   
        cleft-=w[Q[i].ID];   
        b+=p[Q[i].ID];   
        i++;   
    }   
    if(i<=n)   
        b+=1.0*p[Q[i].ID]*cleft/w[Q[i].ID];/*如果不能完整装入一个物品,则可以装入部分。*/  
    return b;   
}   
  
double Knap::knapsack()   
{   
    /*分支判断,如果背包容量足够大,就不用再回溯搜索了。*/  
    int i;   
    double W=0;   
    double P=0;   
    for(i=1;i<=n;i++)   
    {   
        W+=w[i];   
        P+=p[i];   
    }   
    if(W<=c)   //如果背包够大的话那么最优解就是全部了。bestx[]=1;
	{
		for(int j=1;j<=n;j++)
			bestx[j]=1;
		return P;  
	}
/*如果背包容量不够大,则有最有的方案,使用优先队列方法。*/  

    return MaxKnapsack();   
} 

void Knap::AddLiveNode(double up,double cp,double cw,bool ch,int lev)
{
	bbnode *b=new bbnode;   
    b->parent=E;   
    b->LChild=ch;   
    HeapNode N;
	N.uprofit=up;
	N.profit=cp;
	N.weight=cw;   
    N.level=lev;
    N.ptr=b;   
    InsertMax(N,H);   
}
int Knap::MaxKnapsack()
{
	double bestp=0;//当前最优值
	double up=Bound(1);//价值上界
//	cout<<up<<endl;//输出为22
	int i=1;
	while(i != n + 1)
	{
		//double wt=cw+w[i];
		//cout<<wt<<endl;
		if(cw+w[i]<=c)
		{
			if(cp+p[i]>bestp)/*前面是cw+w[i]<=c,这里是当前价值加上这一物品的价值大于最优值时更新最优值bestp*/
				bestp=cp+p[i];
			AddLiveNode(up,cp+p[i],cw+w[i],true,i+1);//激活这一结点
		}
		up=Bound(i+1);
		//检查当前扩展结点的右儿子结点
		if(up>=bestp)
			AddLiveNode(up,cp,cw,false,i+1);
		//取出下一个扩展结点
		 HeapNode N;   
         N=DeleteMax(H);  
		 E=N.ptr;
		 cw=N.weight;
		 cp=N.profit;
		 up=N.uprofit;
		 i=N.level;
	}

	//构造当前左右解
	for(int j=n;j>0;j--)
	{
		bestx[j]=E->LChild;
		E=E->parent;
	}
	return cp;
}

int main()   
{   
    int n=4;   
    double c=7;//背包容量   
/*注意点,这里的数组p[]和w[]的第一个元素是-100,这是因为我在操作过程中都是从数组元素的1开始的,而我们知道数组中第一个元素是0号元素,所以我这里用-100填上*/  
    double p[]={-100,9,10,7,4};//物品价值   
    double w[]={-100,3,5,2,1};//物品重量   
    Knap k=Knap(p,w,c,n);   
    cout<<k.knapsack()<<endl;   
    k.output();   
    return 1;   
}  
</pre><pre class="cpp" name="code">
struct bbnode   
{   
    bbnode *parent;   
    bool LChild;   
};   
  
struct HeapNode   
{   
    bbnode *ptr; //指向活结点在子集树中相应结点的指针  
 
 double weight;  //结点所相应的重量
 double uprofit, //结点的价值上限
  profit;  //结点所相应的价值
    int level;   //活结点在子集树中所处的层序号
};   
/****************************************************************************/  
  
/****************************************************************************/  
typedef HeapNode ElemType;  
#define MaxData 32767   
  
  
typedef struct Heap   
{   
    int capacity;   
    int size;   
    HeapNode *Elem;   
  
}Heap,*HeapQueue;   
  
HeapQueue init(int maxElem)   
{   
    HeapQueue H=new Heap;   
    H->capacity=maxElem;   
    H->size=0;   
    H->Elem=new HeapNode[maxElem+1];   
 H->Elem[0].uprofit=MaxData;   
    return H;   
}   
  
void InsertMax(ElemType x,HeapQueue H)   
{   
    int i;   
    for(i=++H->size;H->Elem[i/2].uprofit<x.uprofit;i/=2)   
        H->Elem[i]=H->Elem[i/2];//此时i还没有进行i/2操作   
    H->Elem[i]=x;   
}   
  
ElemType DeleteMax(HeapQueue H)   
{   
    int i,child;   
    ElemType MaxElem,LastElem;          //存储最大元素和最后一个元素。   
    MaxElem=H->Elem[1];              //堆是从第1号元素开始的。   
    LastElem=H->Elem[ H->size-- ];    //这里自动让size减少了。   
    for(i = 1 ; i * 2 <= H->size ; i = child)   
    {   
        child=i * 2;   
        /*在节点有左右子树的时候,可能存在一个大一个小的情况,这时候我们就要找出最小的;  
          如果Child = H->Size则表明他没有右子树,这时候就没有必要比较了。  
        */  
        if(child != H->size && H->Elem[child+1].uprofit>H->Elem[child].uprofit)   
            child++;//找最大的子树   
        if(LastElem.uprofit < H->Elem[child].uprofit)   
            H->Elem[i]=H->Elem[child];   
    }   
    H->Elem[i]=LastElem;   
    return MaxElem;   
}



你可能感兴趣的:(Algorithm,算法,搜索,指针,heap)