C ++实现背包问题的分支定界算法

8
我正在尝试使用分支和界限法实现这个背包问题的C++版本。在这个网站上有一个Java版本:实现背包问题的分支和界限法
我正在尝试让我的C++版本输出应该输出的90,但它并没有如此,而是输出了5。
有谁知道问题可能出在哪里?
#include <queue>
#include <iostream>
using namespace std;

struct node
{
    int level;
    int profit;
    int weight;
    int bound;
};

int bound(node u, int n, int W, vector<int> pVa, vector<int> wVa)
{
    int j = 0, k = 0;
    int totweight = 0;
    int result = 0;

    if (u.weight >= W)
    {
        return 0;
    }
    else
    {
        result = u.profit;
        j = u.level + 1;
        totweight = u.weight;

        while ((j < n) && (totweight + wVa[j] <= W))
        {
            totweight = totweight + wVa[j];
            result = result + pVa[j];
            j++;
        }

        k = j;

        if (k < n)
        {
            result = result + (W - totweight) * pVa[k]/wVa[k];
        }
        return result;
    }
}

int knapsack(int n, int p[], int w[], int W)
{
    queue<node> Q;
    node u, v;
    vector<int> pV;
    vector<int> wV;
    Q.empty();

    for (int i = 0; i < n; i++)
    {
        pV.push_back(p[i]);
        wV.push_back(w[i]);
    }

    v.level = -1; 
    v.profit = 0;
    v.weight = 0;

    int maxProfit = 0;

    //v.bound = bound(v, n, W, pV, wV);
    Q.push(v);

    while (!Q.empty())
    {
        v = Q.front();
        Q.pop();

        if (v.level == -1)
        {
            u.level = 0;
        }
        else if (v.level != (n - 1))
        {
            u.level = v.level + 1;
        }

        u.weight = v.weight + w[u.level];
        u.profit = v.profit + p[u.level];

        u.bound = bound(u, n, W, pV, wV);

        if (u.weight <= W && u.profit > maxProfit)
        {
            maxProfit = u.profit;
        }

        if (u.bound > maxProfit)
        {
            Q.push(u);
        }

        u.weight = v.weight;
        u.profit = v.profit;

        u.bound = bound(u, n, W, pV, wV);

        if (u.bound > maxProfit)
        {
            Q.push(u);
        }
    }
    return maxProfit;
}

int main()
{
    int maxProfit;
    int n = 4;
    int W = 16;
    int p[4] = {2, 5, 10, 5};
    int w[4] = {40, 30, 50, 10};

    cout << knapsack(n, p, w, W) << endl;

    system("PAUSE");
}

2
请不要在问题解决后仅仅编辑您的问题。 - Xeo
4个回答

5
我认为您已经把利润和重量值放在了错误的向量中。请更改为:
int p[4] = {2, 5, 10, 5};
int w[4] = {40, 30, 50, 10};

to:

int w[4] = {2, 5, 10, 5};
int p[4] = {40, 30, 50, 10};

你的程序将输出90。


5
我认为你所实现的并不是一种分支界定算法,更像是一种基于估计的回溯算法。你使用的数据结构存在问题,你所做的是先将所有第一层推入队列,再将所有第二层推入队列,最后将所有第三层推入队列,并按其插入顺序取回。虽然可以得到结果,但这仅仅是搜索整个搜索空间。
相反,你需要执行的操作是始终在具有最高估计界限的节点上进行分支。换句话说,你总是在占据路径的每个节点上分支,而不管它们的估计边界如何。分支界定技术从每次只分支可能导致结果的单个节点中获得速度优势(具有最高估计值)。
例如,在你的第一次迭代中,假设你发现了两个估计值为:
节点1: 110
节点2: 80
你将它们都推到队列中,使队列变为“n2-n1-head”。在第二次迭代中,你在节点1上完成分支后推入了另外两个节点:
节点3:100
节点4:95
然后你也将它们添加到队列中("n4-n3-n2-head"),这就出现了错误。在下一次迭代中,你将会得到节点2,但实际上应该是节点3,因为它具有最高的估计值。
所以如果我没有漏掉代码中的任何东西,你的实现和Java的实现都是错误的。你应该使用一个优先队列(堆)来实现真正的分支界定算法。

1
        #include <bits/stdc++.h>
using namespace std;

struct Item
{
    float weight;
    int value;
};
struct Node
{
    int level, profit, bound;
    float weight;
};

bool cmp(Item a, Item b)
{
    double r1 = (double)a.value / a.weight;
    double r2 = (double)b.value / b.weight;
    return r1 > r2;
}
int bound(Node u, int n, int W, Item arr[])
{
    if (u.weight >= W)
        return 0;
    int profit_bound = u.profit;
    int j = u.level + 1;
    int totweight = u.weight;

    while ((j < n) && (totweight + arr[j].weight <= W))
    {
        totweight    = totweight + arr[j].weight;
        profit_bound = profit_bound + arr[j].value;
        j++;
    }
    if (j < n)
        profit_bound = profit_bound + (W - totweight) * arr[j].value /
                                         arr[j].weight;

    return profit_bound;
}

int knapsack(int W, Item arr[], int n)
{
    sort(arr, arr + n, cmp);
    queue<Node> Q;
    Node u, v;
    u.level = -1;
    u.profit = u.weight = 0;
    Q.push(u);
    int maxProfit = 0;
    while (!Q.empty())
    {
        u = Q.front();
        Q.pop();
        if (u.level == -1)
            v.level = 0;

        if (u.level == n-1)
            continue;
        v.level = u.level + 1;
        v.weight = u.weight + arr[v.level].weight;
        v.profit = u.profit + arr[v.level].value;
        if (v.weight <= W && v.profit > maxProfit)
            maxProfit = v.profit;
        v.bound = bound(v, n, W, arr);
        if (v.bound > maxProfit)
            Q.push(v);
        v.weight = u.weight;
        v.profit = u.profit;
        v.bound = bound(v, n, W, arr);
        if (v.bound > maxProfit)
            Q.push(v);
    }

    return maxProfit;
}
int main()
{
    int W = 55;   // Weight of knapsack
    Item arr[] = {{10, 60}, {20, 100}, {30, 120}};
    int n = sizeof(arr) / sizeof(arr[0]);

    cout << "Maximum possible profit = "
         << knapsack(W, arr, n);

    return 0;
}
**SEE IF THIS HELPS**

1

您将 W 设置为 16,因此结果为 5。您可以放入背包的唯一物品是利润为 5,重量为 10 的物品 3。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接