01背包问题 -- 回溯法 2

时间:2023-02-14 04:24:27
/*0-1背包伪代码*/
#include <iostream>   
using namespace std;        
template<class Typew,class Typep>    
class Knap    //Knap类记录解空间树的结点信息 
{        
	template<class Typew,class Typep>        
	friend Typep Knapsack(Typep [],Typew [],Typew,int);  //负责对变量初始化,调用递归函数 Backtrack(1)实现回溯搜索并返回最大装包价值
	private:            
		Typep Bound(int i);  //计算以当前结点为根的子树的价值上界        
		void Backtrack(int i);  //核心函数,对解空间树回溯搜索,求得最大装包价值
		Typew c;    //背包容量           
		int n;      //装包物品数量            
		Typew *w;     //物品重量数组          
		Typep *p;     //物品价值数组           
		Typew cw;     //当前重量,从根到当前结点所形成的部分解的装包物品重量      
		Typep cp;       //当前价值,从根到当前结点所形成的部分解的装包物品价值
		Typep bestp;    //当前最优价值,已搜索过的部分解空间树的最优装包价值  
};     
  
template<class Typew,class Typep>    
Typep Knapsack(Typep p[],Typew w[],Typew c,int n);  //声明背包问题求解函数     
template <class Type>    
inline void Swap(Type &a,Type &b);  //声明交换函数     
template<class Type>    
void BubbleSort(Type a[],int n);  //声明冒泡排序函数   
  
int main()   
{        
	int n; //物品数       
	int c; //背包容量        
	cout<<"物品个数为:"; 
	cin>>n;  
	cout<<"背包容量为:";  
	cin>>c;    
	int *p = new int[n];//物品价值 下标从1开始      
	int *w = new int[n];//物品重量 下标从1开始        
	cout<<"物品重量分别为:"<<endl;    
	for(int i=1; i<=n; i++)         
	{            
		cin>>w[i];       
	}   
	cout<<"物品价值分别为:"<<endl;   
	for(int i=1; i<=n; i++)   //以二元组(重量,价值)的形式输出每物品的信息     
	{            
		cin>>p[i];       
	}      
	cout<<"物品重量和价值分别为:"<<endl;        
	for(int i=1; i<=n; i++) //以二元组(重量,价值)的形式输出每个物品的信息    
	{            
		cout<<"("<<w[i]<<","<<p[i]<<") ";
	}        
	cout<<endl;        
	cout<<"背包能装下的最大价值为:"<<Knapsack(p,w,c,n)<<endl; //输出结果  
	system("pause");     
	return 0;   
}       

//核心函数,对解空间树回溯搜索,求得最大装包价值
template<class Typew,class Typep>    
void Knap<Typew,Typep>::Backtrack(int i)   
{        
	if(i>n)      //到达叶子节点       
	{            
		bestp = cp;       //更新最优值         
		return;       
	}         
	if(cw + w[i] <= c)       //满足约束函数,进入左子树       
	{            
		cw += w[i];           
		cp += p[i];            
		Backtrack(i+1);      //回溯还原
		//回溯结束回到当前根结点         
		cw -= w[i];           
		cp -= p[i];       
	}      
	//进入右子树,条件是上界值比当前最优值大,否则就将右子树剪掉  
	if(Bound(i+1)>bestp)     
	{            
		Backtrack(i+1);       
	}   
}     

//计算以当前结点为根的子树的价值上界
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 += cleft /w[i]*p[i];  //则将物品的部分装入到背包中    
	}      
	return b;   
}       

class Object  //定义对象类,作用相当于结构体 
{        
	template<class Typew,class Typep>        
	friend Typep Knapsack(Typep[],Typew [],Typew,int);    
	public:            
		int operator >= (Object a)const  //符号重载函数,重载>=符号        
		{                
			return (d>=a.d);           
		}       
	private:            
		int ID;   //编号          
		float d;   //单位重量的价值    
};
       
template<class Typew,class Typep>    
Typep Knapsack(Typep p[],Typew w[],Typew c,int n)   
{        
	//为Knap::Backtrack初始化       
	Typew W = 0;       
	Typep P = 0;        
	Object *Q = new Object[n];//创建Object类的对象数组|   
	//初始化Object类的对象数组|     
	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<Typew,Typep> K;  //创建Knap的对象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     
	K.cp = 0;       
	K.cw = 0;       
	K.c = c;       
	K.n = n;       
	K.bestp = 0;            
	//对解空间树回溯搜索,求得最大装包价值  
	K.Backtrack(1);       
	delete []Q;       
	delete []K.w;      
	delete []K.p;        
	return K.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(exchange==false)             
			{                 
				break              
			}        
	}
}       
template <class Type>    
inline void Swap(Type &a,Type &b)  //交换函数 
{        
	Type temp = a;       
	a = b;       
	b = temp;   
}