01背包问题 -- 回溯法 2

/*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;   
}

 

转载于:https://my.oschina.net/u/3761238/blog/2695793

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回溯法解0_1背包问题时,会用到状态空间树。在搜索状态空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r≤bestp时,可剪去右子树。计算右子树中解的上界可以用的方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界,用此值来剪枝。 为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考察各物品即可。在实现时,由MaxBoundary函数计算当前结点处的上界。它是类Knap的私有成员。Knap的其他成员记录了解空间树种的节点信息,以减少函数参数的传递以及递归调用时所需要的栈空间。在解空间树的当前扩展结点处,仅当要进入右子树时才计算上界函数MaxBoundary,以判断是否可以将右子树减去。进入左子树时不需要计算上界,因为其上界与父结点的上界相同。 在调用函数Knapsack之前,需要先将各物品依其单位重量价值从达到小排序。为此目的,我们定义了类Objiect。其中,运算符与通常的定义相反,其目的是为了方便调用已有的排序算法。在通常情况下,排序算法将待排序元素从小到大排序。 在搜索状态空间树时,由函数Backtrack控制。在函数中是利用递归调用的方法实现了空间树的搜索
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值