1. 程式人生 > >回溯演算法-子集樹-0-1揹包問題

回溯演算法-子集樹-0-1揹包問題

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