1. 程式人生 > 其它 >非遞歸回溯法實現0-1揹包問題

非遞歸回溯法實現0-1揹包問題

技術標籤:演算法設計與分析c++演算法

來,捏捏大佬牛子: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();
}