回溯法求解0-1背包问题
生活随笔
收集整理的這篇文章主要介紹了
回溯法求解0-1背包问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
回溯法求解0-1背包問題時比較隨機序列和按 v/w 降序排列的算法
問題描述:
針對0-1背包問題,嘗試用回溯法。
物品總數N=10,背包容量 C=26, 物品的重量數組為w={7,3,10,12,1,5,7,3,6, 4}, 相應物品價值數組為:v={20,6,15,20,12,10,17,6,3,10} 。
試比較隨機序列和按 v/w 降序排列的算法訪問節點的個數的差異。
代碼實現:
/* 回溯法背包問題 比較隨機序列和按 v/w 降序排列的算法訪問節點的個數的差異 N=10, C=26, w={7,3,10,12,1,5,7,3,6,4}, v={20,6,15,20,12,10,17,6,3,10} */#include <iostream> #include <algorithm> #include <stdio.h> #include <cstdlib> #include <ctime> int fluet=0; //#include <conio.h> using namespace std; int n=10;//物品數量 double c=26;//背包容量 double w[]={0,7,3,10,12,1,5,7,3,6,4};//各個物品的重量 weight double v[]={0,20,6,15,20,12,10,17,6,3,10}; //各個物品的價值 value //double v[100];//各個物品的價值 value //double w[100];//各個物品的重量 weight double cw = 0;//當前背包重量 current weight double cp = 0;//當前背包中物品總價值 current value double bestp = 0;//當前最優價值best price double perp[100];//單位物品價值(排序后) per price int order[100];//物品編號 int best_x[100];//用于記錄回溯過程的最優情況 int x[100];//設置是否裝入,為1的時候表示選擇該組數據裝入,為0的表示不選擇該組數據//按單位價值排序 void knapsack() {int i,j;int temporder = 0;double temp = 0;for(i=1;i<=n;i++)perp[i]=v[i]/w[i]; //計算單位重量的物品價值for(i=1;i<=n-1;i++){for(j=i+1;j<=n;j++)if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]{temp = perp[i]; //冒泡對perp[]排序perp[i]=perp[j];perp[j]=temp;temporder=order[i];//冒泡對order[]排序order[i]=order[j];order[j]=temporder;temp = v[i];//冒泡對v[]排序v[i]=v[j];v[j]=temp;temp=w[i];//冒泡對w[]排序w[i]=w[j];w[j]=temp;}}cout<<"按單位價值排序后順序:"<<endl;for(i=1;i<=n;i++){cout<<order[i]<<" ";} cout<<endl; }//隨機排序 void knapsack_random() {int i,j;int temporder = 0;double temp = 0;for(i=1;i<=n;i++) {// int j = parseInt(Math.random() * (n - 1));srand((unsigned)time(NULL)); /*播種子,這里用到了time需要包含頭文件time.h*/int j =rand()%n+1;//rand()%n生成0~(n-1)之間隨機整數 temporder=order[i];//對order[]隨機改變順序order[i]=order[j];order[j]=temporder;temp = v[i];//相應對v[]改變順序 v[i]=v[j];v[j]=temp;temp=w[i];//相應對w[]改變順序w[i]=w[j];w[j]=temp;}cout<<"隨機排序后順序:"<<endl;for(i=1;i<=n;i++){cout<<order[i]<<" ";} cout<<endl;}//回溯函數 void backtrack(int i) { //i用來指示到達的層數(第幾步,從0開始),同時也指示當前選擇玩了幾個物品double bound(int i);if(i>n) //遞歸結束的判定條件{for(int i = 1; i<= n; i++)best_x[i] = x[i]; //記錄回溯的最優情況bestp = cp;return;}//如若左子節點可行,則直接搜索左子樹;if(cw+w[i]<=c)//將物品i放入背包,搜索左子樹{fluet++;cw+=w[i];//同步更新當前背包的重量cp+=v[i];//同步更新當前背包的總價值x[i]=1;backtrack(i+1);//深度搜索進入下一層cw-=w[i];//回溯復原cp-=v[i];//回溯復原x[i]=0;}//對于右子樹,先計算上界函數,以判斷是否將其減去if(bound(i+1)>bestp)//如若符合條件則搜索右子樹{fluet++;x[i]=0;backtrack(i+1);} }//計算上界函數,功能為剪枝 double bound(int i) { //判斷當前背包的總價值cp+剩余容量可容納的最大價值<=當前最優價值double leftw= c-cw;//剩余背包容量double b = cp;//記錄當前背包的總價值cp,最后求上界//以物品單位重量價值遞減次序裝入物品while(i<=n && w[i]<=leftw){leftw-=w[i];b+=v[i];i++;}//裝滿背包if(i<=n)b+=v[i]/w[i]*leftw;return b;//返回計算出的上界}int main() {int i;for(i=1;i<=n;i++){order[i]=i;}// 按 v/w 降序排列訪問cout<<"按 v/w 降序排列的算法訪問: "<<endl; knapsack();backtrack(1);cout<<"最優價值為:"<<bestp<<endl;cout<<"需要裝入的物品編號是:";for(i=1;i<=n;i++){if(best_x[i]==1)cout<<order[i]<<" ";}cout<<endl;cout<<"訪問節點數:"<< fluet<<endl;//隨機訪問 fluet=0;cout<<"按隨機序列的算法訪問: "<<endl;knapsack_random();backtrack(1);cout<<"最優價值為:"<<bestp<<endl;cout<<"需要裝入的物品編號是:";for(i=1;i<=n;i++){if(best_x[i]==1)cout<<order[i]<<" ";}cout<<endl;cout<<"訪問節點數:"<< fluet<<endl; return 0; }數據輸出:
結果分析:
從這個數據結果輸出來看,按 v/w 降序排列的算法訪問的程序代碼是沒有問題的。(網上的回溯法我看實質上都是結合貪心做的)。但是很明顯可以看到在按隨機算法訪問序列那里,程序輸出的結果并不準確。個人分析原因是因為在隨機序列算法訪問節點時,未對程序進行排序,則計算bound()函數并未算對,導致右子樹的剪枝不正確。
不太知道怎么改,有沒有大神幫忙修改一下~
總結
以上是生活随笔為你收集整理的回溯法求解0-1背包问题的全部內容,希望文章能夠幫你解決所遇到的問題。