1. 程式人生 > >0-1揹包_回溯法

0-1揹包_回溯法

初始條件如下

看下面的動圖瞭解回溯的過程

 

 對應的解空間和約束條件和狀態樹如下

設當前有N個物品,容量為M; 這些物品要麼選,要麼不選,我們假設選的第一個物品編號為i(1~i-1號物品不選),問題又可以轉化為有N-I個物品(即第I+1~N號物品),容量為M-Wi的子問題……如此反覆下去,然後在所有可行解中選一個效益最大的便可。

回溯(狀態恢復)後,需恢復的狀態有:

Bag[k] 揹包可裝的物品重量:wei:=wei+w[k]*i 已裝物品的價值總和:count:=count-v[k]*i

參考虛擬碼如下

 1 procedure work(k,wei:integer);
2 var i,j:integer; 3 for i:=1 downto 0 do 4 if (wei-w[k]*i>=0) 5 { 6 bag[k]:=i; 7 count:=count+v[k]*i; 8 if (k=n) and (count>best) 9 {best:=count; 10 for j:=1 to n do y[j]:=bag[j]; 11 }
12 if k<n then work(k+1,wei-w[k]*i); 13 count:=count-c[k]*i; {狀態恢復} 14 }

下面是詳細的程式碼

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int v[10];//物品的價值
 4 int bag[10];//記錄解空間物品的狀態1為裝入0為沒有裝入
 5 int w[10];//每個物品的重量
 6 int y[10];//記錄最佳裝載的方案
 7 int count1=0
;//記錄該裝載方案的總價值 8 int best=0;//記錄最佳裝載方案的總價值 9 void procedure(int k,int wei,int N) 10 { 11 12 for(int i=0;i<=1;i++){//0為裝入1位不裝入 13 if(wei-w[k]*i>=0){//如果還有剩餘空間,實現了剪枝 14 bag[k]=i;//記錄該物品是否被裝入 15 count1=count1+v[k]*i;//總價值 16 if(k==N && count1 >best){//此時裝載方案是目前已知的裝載方案最優 17 best=count1;//更新這個最優總價值 18 for(int j=1;j<=N;j++){//記錄此時揹包裝入狀態 19 y[j]=bag[j]; 20 } 21 } 22 if(k<N){//k==n遞迴 23 procedure(k+1,wei-w[k]*i,N); 24 } 25 26 count1-=v[k]*i;//k<n回溯上一步 27 } 28 } 29 } 30 int main() 31 { 32 memset(v,0,sizeof(v)); 33 memset(bag,0,sizeof(bag)); 34 memset(y,0,sizeof(y)); 35 memset(w,0,sizeof(w)); 36 cout << "請輸入物品的總個數(n<=9)" << endl; 37 int n; 38 cin >> n; 39 cout << "請輸入每個物品的重量" << endl; 40 for(int i=1;i<=n;i++){ 41 cin >> w[i]; 42 } 43 cout << "請輸入每個物品的價值" << endl; 44 for(int i=1;i<=n;i++){ 45 cin >> v[i]; 46 } 47 cout << "請輸入該揹包最多可以裝入的物品的總重量" << endl; 48 int wei; 49 cin >> wei; 50 procedure(0,wei,n); 51 cout <<"該揹包可以裝入的最大物品總價值是" << endl; 52 cout << best << endl; 53 cout << "裝入的物品有" << endl; 54 for(int i=1;i<=n;i++){ 55 if(y[i]==1){ 56 cout << "物品" << i << " "; 57 } 58 } 59 }

執行結果如下