分支限界法解01背包问题

发布时间 2023-05-01 21:45:45作者: Jocelynn
#include <iostream>
using namespace std;
#define MAX 100
struct Node {
    int isVisit;//记录节点是否被扩展
    double w;
    double v;
    int level; //记录节点所在的层次
    double ub; //上界
    Node* parent; //父节点
};

double maxValue = 0;
Node* PT[MAX];

Node* maxofPT(int count) { //count代表PT现在有的节点数量,PT数组最后一个节点的下标
    double max = 0; //找最大值
    int r = -1; //r记录找到的最大值对应的PT的下标
    for (int i = 0; i <= count; i++) {
        if (PT[i]->ub > max && PT[i]->isVisit == 0) {
            max = PT[i]->ub;
            r = i;
        }
    }
    PT[r]->isVisit = 1;//即将被扩展
    return PT[r];
}

Node* bestLeaf(int count, int n) {//叶子节点就是所求解的节点
    double max = 0;
    int r = -1;
    for (int i = 0; i <= count; i++) {
        if (PT[i]->ub>max&&PT[i]->level == n) {
            max = PT[i]->ub;
            r = i;
        }
    }
    return PT[r];
}

int* knapsack(double* w, double* v, int n, int capacity) {
    //int x[n]; 这样写会报错“表达式必须具有常量值”
    int* x = (int*)malloc(sizeof(int) * n);//申请一个数组存储最优解
    int count = 0;//PT中现在节点的下标是0
    double lb = 0;//下界值初始化为0
    double weight = 0;//背包中现在装的物品容量为0
    //初始化PT[0]
    Node* initN = (Node*)malloc(sizeof(Node) * 1);
    initN->isVisit = 0;
    initN->v = 0;
    initN->w = 0;
    initN->level = 0;
    initN->ub= capacity*(v[0]/w[0]); //初始上界值
    initN->parent = NULL;
    PT[count] = initN;
    //贪心法求下界
    for (int i = 0; i < n; i++) {
        if (weight + w[i] <= capacity) {
            weight += w[i];
            lb += v[i];
        }
    }
    // 选择扩展节点、剪枝、更新ub的过程如下
    Node* expandNode = (Node*)malloc(sizeof(Node) * 1);
    Node* bestNode = (Node*)malloc(sizeof(Node) * 1);
    while (1) {
        //从PT表中选择一个具有最大上界的节点作为扩展节点
        //cout << "进入循环1次" << endl;
        Node* leftNode = (Node*)malloc(sizeof(Node) * 1); //左边节点
        Node* rightNode = (Node*)malloc(sizeof(Node) * 1); //右边节点
        expandNode = maxofPT(count);
        if (expandNode->level != n) {//如果expandNode->level==n,说明已经是叶子节点,不用再算了
            //计算左节点
            leftNode->isVisit = 0;
            leftNode->v = expandNode->v + v[expandNode->level];
            leftNode->w = expandNode->w + w[expandNode->level];
            leftNode->level = expandNode->level + 1;
            if (leftNode->level != n) {
                leftNode->ub = leftNode->v +(capacity- leftNode->w)* v[leftNode->level] / w[leftNode->level];
            }
            else {
                leftNode->ub = leftNode->v;
            }
            leftNode->parent = expandNode;
            //加入PT表
            if (leftNode->w <= capacity) {
                PT[++count] = leftNode;
            }
            //计算右节点
            rightNode->isVisit = 0;
            rightNode->v = expandNode->v;
            rightNode->w = expandNode->w;
            rightNode->level = expandNode->level + 1;
            if (rightNode->level != n) {
                rightNode->ub = rightNode->v + (capacity - rightNode->w) * v[rightNode->level] / w[rightNode->level];
            }
            else {
                rightNode->ub = rightNode->v;
            }
            rightNode->parent = expandNode;
            //加入PT表
            if (rightNode->w <= capacity) {
                PT[++count] = rightNode;
            }
        }
        else {
            break;
        }
    }//while
    bestNode = bestLeaf(count, n);//找到解
    maxValue = bestNode->v;
    //回溯法找到物品选择情况
    for (int i = n-1; i >= 0; i--) {
        if (bestNode->v == bestNode->parent->v) {
            x[i] = 0; 
        }
        else {
            x[i] = 1;
        }
        bestNode = bestNode->parent;//向上一层回溯
    }
    return x;
}
int main() {
    double w[]={2,2,3,1,5,2};
    double v[]={2,3,1,5,4,3};
    int c = 10;
    int n = 6;
    int* ans = (int*)malloc(sizeof(int) * n);
    ans = knapsack(w, v, n, c);
    cout << "最大价值: " << maxValue << endl;
    cout << "最优解" << endl;
    for (int i = 0; i < n; i++) {
        cout << ans[i] << endl;
    }
    system("pause");
    return 0;
}