1. 程式人生 > >01揹包問題 -- 回溯法 2

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