📜  0/1 背包使用最低成本分支和边界

📅  最后修改于: 2021-10-28 01:52:24             🧑  作者: Mango

给定N个重量为W[0..n-1] 的物品,值V[0..n-1]和一个容量为C的背包,选择这样的物品:

  1. 装入背包的重量总和小于或等于 C。
  2. 在所有可能的组合中,背包中物品的价值总和最大。

例子:

方法:
在这篇文章中,讨论了对 0/1 背包问题使用最小成本(LC)分支定界方法的实现。
可以使用FIFOLIFOLC策略解决分支限界问题。最低成本(LC)被认为是最智能的,因为它根据启发式成本函数选择下一个节点。它选择成本最低的那个。
由于0/1 Knapsack是关于最大化总值,我们不能直接使用LC Branch and Bound技术来解决这个问题。相反,我们通过取给定值的负数将其转换为最小化问题。
请按照以下步骤解决问题:

  1. 根据物品的价值/重量(V/W)比率对物品进行排序。
  2. 在优先级队列中插入一个虚拟节点。
  3. 重复以下步骤,直到优先级队列为空:
    • 从优先级队列中提取 peek 元素并将其分配给当前节点
    • 如果当前节点的上限小于所有探索的节点的最小下限minLB ,则没有探索点。所以,继续下一个元素。不考虑上限大于minLB的节点的原因是,上限存储了可能达到的最佳值。如果最佳值本身不是最佳值minLB ,那么探索该路径是没有用的。
    • 更新路径数组
    • 如果当前节点的级别为N ,则检查当前节点的下限是否小于finalLB ,即所有到达最终级别的路径的最小下限。如果为真,则更新finalPathfinalLB 。否则,继续下一个元素。
    • 计算当前节点的右孩子的下界和上界。
    • 如果当前项可以插入背包,则计算当前节点的左子节点的下界和上界。
    • 更新minLB并在其上限小于minLB 时插入子

下面是上述方法的实现:

C++
// C++ Program to implement 0/1
// knapsack using LC Branch and Bound
 
#include 
using namespace std;
 
// Stores the number of items
int size;
 
// Stores the knapsack capacity
float capacity;
 
typedef struct Item {
 
    // Stores the weight of items
    float weight;
 
    // Stores the value of items
    int value;
 
    // Stores the index of items
    int idx;
} Item;
 
typedef struct Node {
 
    // Upper Bound: Best case
    // (Fractional Knapsck)
    float ub;
 
    // Lower Bound: Worst case (0/1)
    float lb;
 
    // Level of the node
    // in the decision tree
    int level;
 
    // Stores if the current item is
    // selected or not
    bool flag;
 
    // Total Value: Stores the sum of the
    // values of the items included
    float tv;
 
    // Total Weight: Stores the sum of the
    // weights of the items included
    float tw;
} Node;
 
// Function to calculate upper bound
// (includes fractional part of the items)
float upper_bound(float tv, float tw,
                  int idx, vector& arr)
{
    float value = tv;
    float weight = tw;
    for (int i = idx; i < size; i++) {
        if (weight + arr[i].weight
            <= capacity) {
            weight += arr[i].weight;
            value -= arr[i].value;
        }
        else {
            value -= (float)(capacity
                             - weight)
                     / arr[i].weight
                     * arr[i].value;
            break;
        }
    }
    return value;
}
 
// Function to calculate lower bound (doesn't
// include fractional part of the items)
float lower_bound(float tv, float tw,
                  int idx, vector& arr)
{
    float value = tv;
    float weight = tw;
    for (int i = idx; i < size; i++) {
        if (weight + arr[i].weight
            <= capacity) {
            weight += arr[i].weight;
            value -= arr[i].value;
        }
        else {
            break;
        }
    }
    return value;
}
 
class comp {
public:
    bool operator()(Node a, Node b)
    {
        return a.lb > b.lb;
    }
};
 
void assign(Node& a, float ub, float lb,
            int level, bool flag,
            float tv, float tw)
{
    a.ub = ub;
    a.lb = lb;
    a.level = level;
    a.flag = flag;
    a.tv = tv;
    a.tw = tw;
}
 
void knapsack(vector& arr)
{
 
    // Sort the items based on the
    // profit/weight ratio
    sort(arr.begin(), arr.end(),
         [&](Item& a, Item& b) {
             return a.value / a.weight
                    > b.value / b.weight;
         });
 
    // min_lb -> Minimum lower bound
    // of all the nodes explored
 
    // final_lb -> Minimum lower bound
    // of all the paths that reached
    // the final level
    float min_lb = 0, final_lb = INT_MAX;
 
    // curr_path -> Boolean array to store
    // at every index if the element is
    // included or not
 
    // final_path -> Boolean array to store
    // the result of selection array when
    // it reached the last level
    bool curr_path[size], final_path[size];
 
    // Priority queue to store the nodes
    // based on lower bounds
    priority_queue,
                   comp>
        pq;
 
    Node current, left, right;
    current.lb = current.ub = current.tw
        = current.tv = current.level
        = current.flag = 0;
 
    // Insert a dummy node
    pq.push(current);
 
    for (int i = 0; i < size; i++)
        curr_path[i] = final_path[i]
            = false;
 
    while (!pq.empty()) {
        current = pq.top();
        pq.pop();
 
        if (current.ub > min_lb
            || current.ub >= final_lb) {
 
            // If the current node's best case
            // value is not optimal than min_lb,
            // then there is no reason to explore
            // that path including final_lb
            // eliminates all those paths whose
            // best values is equal to final_lb
            continue;
        }
 
        // update the path
        if (current.level != 0)
            curr_path[current.level - 1]
                = current.flag;
 
        if (current.level == size) {
            // Reached last level
            if (current.lb < final_lb)
                for (int i = 0; i < size; i++)
                    final_path[arr[i].idx]
                        = curr_path[i];
            final_lb = min(current.lb, final_lb);
            continue;
        }
 
        int level = current.level;
 
        // right node -> Exludes current item
        // Hence, cp, cw will obtain the value
        // of that of parent
        assign(right,
               upper_bound(current.tv,
                           current.tw, level + 1,
                           arr),
               lower_bound(current.tv, current.tw,
                           level + 1, arr),
               level + 1, false,
               current.tv, current.tw);
 
        // Check whether adding the current
        // item will not exceed the knapsack weight
        if (current.tw + arr[current.level].weight
            <= capacity) {
 
            // left node -> includes current item
            // c and lb should be calculated
            // including the current item.
            left.ub
                = upper_bound(
                    current.tv
                        - arr[level].value,
                    current.tw
                        + arr[level].weight,
                    level + 1, arr);
 
            left.lb
                = lower_bound(
                    current.tv
                        - arr[level].value,
                    current.tw
                        + arr[level].weight,
                    level + 1, arr);
 
            assign(left, left.ub, left.lb,
                   level + 1, true,
                   current.tv - arr[level].value,
                   current.tw
                       + arr[level].weight);
        }
 
        // If Left node cannot be inserted
        else {
 
            // Stop the left node from
            // getting added to the
            // priority queue
            left.ub = left.lb = 1;
        }
 
        // Update the lower bound
        min_lb = min(min_lb, left.lb);
        min_lb = min(min_lb, right.lb);
 
        // Exploring nodes whose
        // upper bound is greater than
        // min_lb will never give
        // the optimal result
 
        if (min_lb >= left.ub)
            pq.push(left);
        if (min_lb >= right.ub)
            pq.push(right);
    }
 
    cout << "Items taken into the"
         << " knapsack are : \n";
    if (final_lb == INT_MAX)
        final_lb = 0;
    for (int i = 0; i < size; i++)
        cout << final_path[i] << " ";
    cout << "\n";
    cout << "Maximum profit is : "
         << (-final_lb) << "\n";
}
 
// Driver Code
int main()
{
    size = 4;
 
    capacity = 15;
 
    vector arr;
    arr.push_back({ 2, 10, 0 });
    arr.push_back({ 4, 10, 1 });
    arr.push_back({ 6, 12, 2 });
    arr.push_back({ 9, 18, 3 });
 
    knapsack(arr);
 
    return 0;
}


Java
// Java Program to implement
// 0/1 knapsack using LC
// Branch and Bound
 
import java.util.*;
class Item {
 
    // Stores the weight
    // of items
    float weight;
 
    // Stores the values
    // of items
    int value;
 
    // Stores the index
    // of items
    int idx;
    public Item() {}
    public Item(int value, float weight,
                int idx)
    {
        this.value = value;
        this.weight = weight;
        this.idx = idx;
    }
}
 
class Node {
    // Upper Bound: Best case
    // (Fractional Knapsck)
    float ub;
 
    // Lower Bound: Worst case
    // (0/1)
    float lb;
 
    // Level of the node in
    // the decision tree
    int level;
 
    // Stores if the current
    // item is selected or not
    boolean flag;
 
    // Total Value: Stores the
    // sum of the values of the
    // items included
    float tv;
 
    // Total Weight: Stores the sum of
    // the weights of included items
    float tw;
    public Node() {}
    public Node(Node cpy)
    {
        this.tv = cpy.tv;
        this.tw = cpy.tw;
        this.ub = cpy.ub;
        this.lb = cpy.lb;
        this.level = cpy.level;
        this.flag = cpy.flag;
    }
}
 
// Comparator to sort based on lower bound
class sortByC implements Comparator {
    public int compare(Node a, Node b)
    {
        boolean temp = a.lb > b.lb;
        return temp ? 1 : -1;
    }
}
 
class sortByRatio implements Comparator {
    public int compare(Item a, Item b)
    {
        boolean temp = (float)a.value
                           / a.weight
                       > (float)b.value
                             / b.weight;
        return temp ? -1 : 1;
    }
}
 
class knapsack {
 
    private static int size;
    private static float capacity;
 
    // Function to calculate upper bound
    // (includes fractional part of the items)
    static float upperBound(float tv, float tw,
                            int idx, Item arr[])
    {
        float value = tv;
        float weight = tw;
        for (int i = idx; i < size; i++) {
            if (weight + arr[i].weight
                <= capacity) {
                weight += arr[i].weight;
                value -= arr[i].value;
            }
            else {
                value -= (float)(capacity
                                 - weight)
                         / arr[i].weight
                         * arr[i].value;
                break;
            }
        }
        return value;
    }
 
    // Calculate lower bound (doesn't
    // include fractional part of items)
    static float lowerBound(float tv, float tw,
                            int idx, Item arr[])
    {
        float value = tv;
        float weight = tw;
        for (int i = idx; i < size; i++) {
            if (weight + arr[i].weight
                <= capacity) {
                weight += arr[i].weight;
                value -= arr[i].value;
            }
            else {
                break;
            }
        }
        return value;
    }
 
    static void assign(Node a, float ub, float lb,
                       int level, boolean flag,
                       float tv, float tw)
    {
        a.ub = ub;
        a.lb = lb;
        a.level = level;
        a.flag = flag;
        a.tv = tv;
        a.tw = tw;
    }
 
    public static void solve(Item arr[])
    {
        // Sort the items based on the
        // profit/weight ratio
        Arrays.sort(arr, new sortByRatio());
 
        Node current, left, right;
        current = new Node();
        left = new Node();
        right = new Node();
 
        // min_lb -> Minimum lower bound
        // of all the nodes explored
 
        // final_lb -> Minimum lower bound
        // of all the paths that reached
        // the final level
        float minLB = 0, finalLB
                         = Integer.MAX_VALUE;
        current.tv = current.tw = current.ub
            = current.lb = 0;
        current.level = 0;
        current.flag = false;
 
        // Priority queue to store elements
        // based on lower bounds
        PriorityQueue pq
            = new PriorityQueue(
                new sortByC());
 
        // Insert a dummy node
        pq.add(current);
 
        // curr_path -> Boolean array to store
        // at every index if the element is
        // included or not
 
        // final_path -> Boolean array to store
        // the result of selection array when
        // it reached the last level
        boolean currPath[] = new boolean[size];
        boolean finalPath[] = new boolean[size];
 
        while (!pq.isEmpty()) {
            current = pq.poll();
            if (current.ub > minLB
                || current.ub >= finalLB) {
                // if the current node's best case
                // value is not optimal than minLB,
                // then there is no reason to
                // explore that node. Including
                // finalLB eliminates all those
                // paths whose best values is equal
                // to the finalLB
                continue;
            }
 
            if (current.level != 0)
                currPath[current.level - 1]
                    = current.flag;
 
            if (current.level == size) {
                if (current.lb < finalLB) {
                    // Reached last level
                    for (int i = 0; i < size; i++)
                        finalPath[arr[i].idx]
                            = currPath[i];
                    finalLB = current.lb;
                }
                continue;
            }
 
            int level = current.level;
 
            // right node -> Exludes current item
            // Hence, cp, cw will obtain the value
            // of that of parent
            assign(right, upperBound(current.tv,
                                     current.tw,
                                     level + 1, arr),
                   lowerBound(current.tv, current.tw,
                              level + 1, arr),
                   level + 1, false,
                   current.tv, current.tw);
 
            if (current.tw + arr[current.level].weight
                <= capacity) {
 
                // left node -> includes current item
                // c and lb should be calculated
                // including the current item.
                left.ub = upperBound(
                    current.tv
                        - arr[level].value,
                    current.tw
                        + arr[level].weight,
                    level + 1, arr);
                left.lb = lowerBound(
                    current.tv
                        - arr[level].value,
                    current.tw
                        + arr[level].weight,
                    level + 1,
                    arr);
                assign(left, left.ub, left.lb,
                       level + 1, true,
                       current.tv - arr[level].value,
                       current.tw
                           + arr[level].weight);
            }
 
            // If the left node cannot
            // be inserted
            else {
 
                // Stop the left node from
                // getting added to the
                // priority queue
                left.ub = left.lb = 1;
            }
 
            // Update minLB
            minLB = Math.min(minLB, left.lb);
            minLB = Math.min(minLB, right.lb);
 
            if (minLB >= left.ub)
                pq.add(new Node(left));
            if (minLB >= right.ub)
                pq.add(new Node(right));
        }
        System.out.println("Items taken"
                           + "into the knapsack are");
        for (int i = 0; i < size; i++) {
            if (finalPath[i])
                System.out.print("1 ");
            else
                System.out.print("0 ");
        }
        System.out.println("\nMaximum profit"
                           + " is " + (-finalLB));
    }
 
    // Driver code
    public static void main(String args[])
    {
        size = 4;
        capacity = 15;
 
        Item arr[] = new Item[size];
        arr[0] = new Item(10, 2, 0);
        arr[1] = new Item(10, 4, 1);
        arr[2] = new Item(12, 6, 2);
        arr[3] = new Item(18, 9, 3);
 
        solve(arr);
    }
}


输出:
Items taken into the knapsack are : 
1 1 0 1 
Maximum profit is : 38

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程