wizen-coder
10/4/2018 - 2:17 PM

 问题描述      给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大?      形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找一n元向量(x1,x2,…,xn,)

问题描述 给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找一n元向量(x1,x2,…,xn,), xi∈{0,1}, ∋ ∑ wi xi≤c,且∑ vi xi达最大.即一个特殊的整数规划问题。 算法设计 首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。在优先队列分支限

//0-1背包问题 分支限界法求解 
#include "stdafx.h"
#include "MaxHeap.h"
#include <iostream>
using namespace std;
 
class Object
{
	template<class Typew,class Typep>
	friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);
	public:
		int operator <= (Object a) const
		{
			return d>=a.d;
		}
	private:
		int ID;
		float d;//单位重量价值
};
 
template<class Typew,class Typep> class Knap;
 
class bbnode
{
	friend Knap<int,int>;
	template<class Typew,class Typep>
	friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);
	private:
		bbnode * parent;	//指向父节点的指针
		bool LChild;		//左儿子节点标识
};
 
template<class Typew,class Typep>
class HeapNode
{
	friend Knap<Typew,Typep>;
	public:
		operator Typep() const
		{
			return uprofit;
		}
	private:
		Typep uprofit,		//节点的价值上界
			  profit;		//节点所相应的价值
		Typew weight;		//节点所相应的重量
		int level;			//活节点在子集树中所处的层序号
		bbnode *ptr;		//指向活节点在子集中相应节点的指针
};
 
template<class Typew,class Typep>  
class Knap  
{  
	template<class Typew,class Typep>
	friend Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[]);
	public:
		Typep MaxKnapsack();
    private:  
		MaxHeap<HeapNode<Typep,Typew>> *H;
        Typep Bound(int i);
		void AddLiveNode(Typep up,Typep cp,Typew cw,bool ch,int lev);
 
		bbnode *E;	//指向扩展节点的指针
        Typew c;    //背包容量  
        int n;      //物品数  
  
        Typew *w;   //物品重量数组  
        Typep *p;   //物品价值数组  
        Typew cw;   //当前重量  
  
        Typep cp;   //当前价值  
        int *bestx; //最优解  
};  
 
template <class Type>  
inline void Swap(Type &a,Type &b);  
  
template<class Type>  
void BubbleSort(Type a[],int n); 
 
int main()
{
	int n = 3;//物品数  
    int c = 30;//背包容量  
    int p[] = {0,45,25,25};//物品价值 下标从1开始  
    int w[] = {0,16,15,15};//物品重量 下标从1开始  
	int bestx[4];//最优解
  
    cout<<"背包容量为:"<<c<<endl;  
    cout<<"物品重量和价值分别为:"<<endl;  
  
    for(int i=1; i<=n; i++)  
    {  
        cout<<"("<<w[i]<<","<<p[i]<<") ";  
    }  
    cout<<endl;  
  
    cout<<"背包能装下的最大价值为:"<<Knapsack(p,w,c,n,bestx)<<endl;  
	cout<<"此背包问题最优解为:"<<endl;
	for(int i=1; i<=n; i++)
	{
		cout<<bestx[i]<<" ";
	}
	cout<<endl;
    return 0;  
}
 
template<class Typew,class Typep>
Typep Knap<Typew,Typep>::Bound(int i)//计算节点所相应价值的上界
{
	Typew cleft = c-cw;	//剩余容量高
	Typep b = cp;		//价值上界
	//以物品单位重量价值递减序装填剩余容量
	while(i<=n && w[i]<=cleft)
	{
		cleft -= w[i];
		b += p[i];
		i++;
 	}
 
	//装填剩余容量装满背包
	if(i<=n)
	{
		b += p[i]/w[i]*cleft;
	}
 
	return b;
}
 
//将一个活节点插入到子集树和优先队列中
template<class Typew,class Typep>
void Knap<Typew,Typep>::AddLiveNode(Typep up,Typep cp,Typew cw,bool ch,int lev)
{
	bbnode *b = new bbnode;
	b->parent = E;
	b->LChild = ch;
 
	HeapNode<Typep,Typew> N;
	N.uprofit = up;
	N.profit = cp;
	N.weight = cw;
	N.level  = lev;
	N.ptr = b;
 
	H->Insert(N);
}
 
//优先队列式分支限界法,返回最大价值,bestx返回最优解
template<class Typew,class Typep>
Typep Knap<Typew,Typep>::MaxKnapsack()
{
	H = new MaxHeap<HeapNode<Typep,Typew>>(1000);
 
	//为bestx分配存储空间
	bestx = new int[n+1];
 
	//初始化
	int i = 1;
	E = 0;
	cw = cp = 0;			
	Typep bestp = 0;//当前最优值
	Typep up = Bound(1);	//价值上界
 
	//搜索子集空间树
	while(i!=n+1)
	{
		//检查当前扩展节点的左儿子节点
		Typew wt = cw + w[i];
		if(wt <= c)//左儿子节点为可行节点
		{
			if(cp+p[i]>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<Typep,Typew> N;
		H->DeleteMax(N);
		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;
}
 
//返回最大价值,bestx返回最优解
template<class Typew,class Typep>
Typep Knapsack(Typep p[],Typew w[],Typew c,int n, int bestx[])
{
	//初始化
	Typew W = 0;	//装包物品重量
	Typep P = 0;	//装包物品价值
	
	//定义依单位重量价值排序的物品数组
	Object *Q  = new Object[n];
	for(int i=1; i<=n; i++)
	{
		//单位重量价值数组
		Q[i-1].ID = i;
		Q[i-1].d = 1.0*p[i]/w[i];
		P += p[i];
		W += w[i];
	}
 
	if(W<=c)
	{
		return P;//所有物品装包
	}
 
	//依单位价值重量排序
	BubbleSort(Q,n);
 
	//创建类Knap的数据成员
	Knap<Typew,Typep> K;
	K.p = new Typep[n+1];
	K.w = new Typew[n+1];
 
	for(int i=1; i<=n; i++)
	{
		K.p[i]  = p[Q[i-1].ID];
		K.w[i] = w[Q[i-1].ID];
	}
 
	K.cp = 0;
	K.cw = 0;
	K.c = c;
	K.n = n;
 
	//调用MaxKnapsack求问题的最优解
	Typep bestp = K.MaxKnapsack();
	for(int j=1; j<=n; j++)
	{
		bestx[Q[j-1].ID] = K.bestx[j];
	}
 
	delete Q;
	delete []K.w;
	delete []K.p;
	delete []K.bestx;
	return bestp;	
}
 
template<class Type>  
void BubbleSort(Type a[],int n)  
{  
     //记录一次遍历中是否有元素的交换     
     bool exchange;    
     for(int i=0; i<n-1;i++)    
     {    
        exchange = false ;    
        for(int j=i+1; j<=n-1; j++)    
        {    
            if(a[j]<=a[j-1])    
            {    
                Swap(a[j],a[j-1]);   
                exchange = true;    
            }     
        }     
        //如果这次遍历没有元素的交换,那么排序结束     
        if(false == exchange)    
        {  
             break ;    
        }  
     }  
}  
  
template <class Type>  
inline void Swap(Type &a,Type &b)  
{  
    Type temp = a;  
    a = b;  
    b = temp;  
}  
template<class T>
class MaxHeap
{
	public:
		MaxHeap(int MaxHeapSize = 10);
		~MaxHeap() {delete [] heap;}
        int Size() const {return CurrentSize;}
 
        T Max() 
		{          //查
           if (CurrentSize == 0)
		   {
                throw OutOfBounds();
		   }
           return heap[1];
        }
 
		MaxHeap<T>& Insert(const T& x); //增
		MaxHeap<T>& DeleteMax(T& x);   //删
 
		void Initialize(T a[], int size, int ArraySize);
 
	private:
		int CurrentSize, MaxSize;
		T *heap;  // element array
};
 
template<class T>
MaxHeap<T>::MaxHeap(int MaxHeapSize)
{// Max heap constructor.
	MaxSize = MaxHeapSize;
	heap = new T[MaxSize+1];
	CurrentSize = 0;
}
 
template<class T>
MaxHeap<T>& MaxHeap<T>::Insert(const T& x)
{// Insert x into the max heap.
	if (CurrentSize == MaxSize)
	{
		cout<<"no space!"<<endl; 
		return *this; 
	}
 
    // 寻找新元素x的位置
    // i——初始为新叶节点的位置,逐层向上,寻找最终位置
	int i = ++CurrentSize;
	while (i != 1 && x > heap[i/2])
	{
		// i不是根节点,且其值大于父节点的值,需要继续调整
		heap[i] = heap[i/2]; // 父节点下降
		i /= 2;              // 继续向上,搜寻正确位置
    }
 
   heap[i] = x;
   return *this;
}
 
template<class T>
MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)
{// Set x to max element and delete max element from heap.
	// check if heap is empty
	if (CurrentSize == 0)
	{
		cout<<"Empty heap!"<<endl; 
		return *this; 
	}
 
	x = heap[1]; // 删除最大元素
	// 重整堆
	T y = heap[CurrentSize--]; // 取最后一个节点,从根开始重整
 
	// find place for y starting at root
	int i = 1,  // current node of heap
	   ci = 2; // child of i
 
	while (ci <= CurrentSize) 
    {
		// 使ci指向i的两个孩子中较大者
		if (ci < CurrentSize && heap[ci] < heap[ci+1])
		{
			ci++;
		}
		// y的值大于等于孩子节点吗?
		if (y >= heap[ci])
		{
			break;   // 是,i就是y的正确位置,退出
		}
 
		// 否,需要继续向下,重整堆
		heap[i] = heap[ci]; // 大于父节点的孩子节点上升
		i = ci;             // 向下一层,继续搜索正确位置
		ci *= 2;
    }
 
	heap[i] = y;
	return *this;
}
 
template<class T>
void MaxHeap<T>::Initialize(T a[], int size,int ArraySize)
{// Initialize max heap to array a.
	delete [] heap;
	heap = a;
	CurrentSize = size;
	MaxSize = ArraySize;
 
	// 从最后一个内部节点开始,一直到根,对每个子树进行堆重整
   for (int i = CurrentSize/2; i >= 1; i--)
   {
		T y = heap[i]; // 子树根节点元素
		// find place to put y
		int c = 2*i; // parent of c is target
                   // location for y
		while (c <= CurrentSize) 
		{
			// heap[c] should be larger sibling
			if (c < CurrentSize && heap[c] < heap[c+1])
			{
				c++;
			}
			// can we put y in heap[c/2]?
			if (y >= heap[c])
			{
				break;  // yes
			}
 
			// no
			heap[c/2] = heap[c]; // move child up
			c *= 2; // move down a level
        }
		heap[c/2] = y;
	}
}