01揹包問題 -- 回溯法 2
阿新 • • 發佈:2018-11-04
/*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; }