#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;
}