非遞歸回溯法實現0-1揹包問題
阿新 • • 發佈:2020-12-22
來,捏捏大佬牛子:https://blog.csdn.net/peachhhh/article/details/111475680
#include<iostream> #include<algorithm> using namespace std; class Things; bool cmp(Things a,Things b); class Things{ //物品類 public: double w; //每個物品的重量 double v; //每個物品的價值 double ratio; //每個物品的單位重量價值 friend bool cmp(Things a,Things b){ //按照單位重量價值排序 return a.ratio > b.ratio; } }; class Knap{ //揹包類 public: Things *t; //物品陣列 int *temp_step; //當前容納方案 int *best_step; //最優容納方案 int *cnt; //每層記錄進入兒子種類 double bestv; //最優價值 int num; //物品數量 double c; //揹包容量 public: /* 揹包初始化 */ void Initial(){ cout<<"please input the number of each things and the capacility of kanpsack: "<<endl; cin>>num>>c; bestv = 0; t = new Things[num * 2]; temp_step = new int[num * 2]; best_step = new int[num * 2]; cnt = new int[num * 2]; for(int i = 0;i <= num;i++)cnt[i] = 0; cout<<"please input the value of each thing: "<<endl; for(int i = 0;i < num;i++)cin>>t[i].v; cout<<"please input the weight of each thing: "<<endl; for(int i = 0;i < num;i++){ cin>>t[i].w; t[i].ratio = t[i].v / t[i].w; } sort(t,t + num,cmp); } /* 記錄最優方案 */ void Save(){ bestv = 0; for(int i = 0;i <= num;i++){ best_step[i] = temp_step[i]; bestv += temp_step[i] * t[i].v; } } /* 約束函式 */ bool Limit(int depth){ double tempc = 0; for(int i = 0;i < depth;i++)tempc += (temp_step[i] * t[i].w); if(tempc + t[depth].w <= c)return true; else return false; } /* 限界函式 */ bool Bound(int depth){ double tempv = 0; double tempc = 0; int i; for(i = 0;i < depth;i++){ tempc += temp_step[i] * t[i].w; tempv += temp_step[i] * t[i].v; } for(i = depth + 1;i < num && tempc + t[i].w <= c;i++){ tempc += t[i].w; tempv += t[i].v; } if(i <= num)tempv += ((c - tempc)/t[i].w) * t[i].v; if(tempv > bestv)return true; else return false; } /* 揹包非遞迴 */ void Knapsack_WithoutRecursion(){ int depth = 0; //當前訪問的層數 while(depth >= 0){ //當層數 < 0時退出根節點即推出迴圈,函式結束 if(cnt[depth] <= 1){ /* cnt陣列儲存訪問該層節點的次數:當cnt[depth]為0時即第一次訪問,需進入左子樹; * 當cnt[depth]為1時即第二次訪問需進入右子樹 * 當cnt[depth]大於1時說明該節點的左右子樹均被訪問,退出該層節點depth-- * */ for(int i = cnt[depth];i <= 1;i++){ //即從 cnt[depth]從0 ~ 1依次查詢左右子樹 if(depth == num){ //當達到葉節點時即找到最優解,將最優解儲存下來,然後按照dfs規則返回上一個節點 Save(); depth--; } int child = 1 - cnt[depth]; /*當cnt[depth]為0時,child為1即訪問左子樹 * 當cnt[depth]為1時,child為0即訪問右子樹*/ if(child){ //進入左子樹 if(Limit(depth)){ //約束函式判斷 temp_step[depth] = child; cnt[depth]++; //訪問次數自增 depth++; //向左子樹探索 } else cnt[depth]++; // 若左子樹不合法,但是這也說明左子樹已被訪問,也許自增來達到訪問右子樹的條件 } else{ if(Bound(depth)){ //限界函式判斷 temp_step[depth] = child; cnt[depth]++; //訪問次數自增 depth++; //向右子樹探索 } else cnt[depth]++; // 大坑勿跳,操 } ]} } else{ //當cnt[depth] > 1時即cnt[depth] == 2時,說明該節點的左右子樹均已被訪問則按照dfs規則向上回溯,返回上一層 cnt[depth] = 0; //將cnt[depth]重新設定為0,使得下一次進入該層使為第一次進入. depth--; } } } /* 結果顯示 */ void Show(){ bestv = 0; for(int i = 0;i < num;i++) if(best_step[i]){ cout<<"no."<<i<<" in the knapsack."<<endl; bestv += t[i].v; } cout<<"the bestv is: "<<bestv<<endl; } }; Knap k; int main(){ k.Initial(); k.Knapsack_WithoutRecursion(); k.Show(); }