回溯演算法-子集樹-0-1揹包問題
阿新 • • 發佈:2019-02-10
0-1揹包: 即每種物品只有2 種選擇,分別為:裝入揹包或不裝入揹包,物品數和揹包容量已給定,計算裝入揹包物品的最大價值和最優裝入方案,用回溯法搜尋子集樹的演算法進行求解。對此模型我們剛好建立二叉樹( 此處為完全二叉樹)。對應的葉子節點數為: n! (n為頂點數)。
解子集樹:
約束函式: cw+w[i]*x[i]<=c。(cw:當前揹包重量; w[i]*x[i]選中揹包重量; c:揹包容量)
深度探索截止條件:i>n。(i:當前節點; n:節點總數)
子集樹結構:
思想: 對於每個物品若符合約束函式將當前節點加入到活動節點中 ,繼續深度探索。若不符合直接將當前節點以及子樹"剪枝"處理。當深度探索到葉子節點時。記錄下此時揹包最優值和對應的物品選擇情況。然後從當前葉子節點往上回溯,重複剛才的回溯方法 。注意: 回溯要回溯到根。再由根探索樹的另一邊子樹。當所有節點路徑回溯完即可解決問題。
回溯方法程式碼:
程式碼 :void backpack(int i){ if(i>n){ if(cp>bestp){ bestp = cp; for(int i = 1;i<=n;i++){ //到達葉子節點處物體的選擇情況 order[i] = x[i]; } } }else{ for(int j = 0;j<=1;j++){ //列舉物體i所有可能的路徑, x[i] = j; if(cw+w[i]*x[i]<=c){ //滿足約束,繼續向子節點探索 cp+=p[i]*x[i]; cw+=w[i]*x[i]; backpack(i+1); cp-=p[i]*x[i]; //回到上一層物體的選擇情況 cw-=w[i]*x[i]; } } } }
/** @回溯-0-1揹包 */ #include<iostream> #include<algorithm> #define MAX 100 using namespace std; int n; //揹包數量 int c; //揹包容量 int w[MAX]; //揹包內物體重量 int p[MAX]; //揹包內物體價值 int cp = 0; //當前揹包價值 int cw = 0; //當前揹包重量 int bestp = 0; //最優揹包價值 int x[MAX]; //揹包內物品進行選擇 int order[MAX]; //物品最終選中情況 void backpack(int i){ if(i>n){ if(cp>bestp){ bestp = cp; for(int i = 1;i<=n;i++){ //到達葉子節點處物體的選擇情況 order[i] = x[i]; } } }else{ for(int j = 0;j<=1;j++){ //列舉物體i所有可能的路徑, x[i] = j; if(cw+w[i]*x[i]<=c){ //約束為當前選擇物品的重量不能超過揹包剩餘重量 cp+=p[i]*x[i]; cw+=w[i]*x[i]; backpack(i+1); cp-=p[i]*x[i]; //回到上一層物體的選擇情況 cw-=w[i]*x[i]; } } } } int main(){ cout<<"請輸入物品個數和揹包最大容量:"; cin>>n>>c; cout<<"輸入各物品的重量和價值"<<endl; for(int i = 1;i<=n;i++){ cin>>w[i]>>p[i]; } backpack(1); // 第一件物品預設為選中 cout<<"最優價值為:"<<bestp<<endl; cout<<"物品的選中情況(選中為1,沒有為0)"<<endl; for(int i = 1;i<=n;i++){ cout<<order[i]<<" 選中的編號為:"<<i<<endl; } return 0; } /* 4 7 3 9 5 10 2 7 1 4 */