给定N个重量为W[0..n-1] 的物品,值V[0..n-1]和一个容量为C的背包,选择这样的物品:
- 装入背包的重量总和小于或等于 C。
- 在所有可能的组合中,背包中物品的价值总和最大。
例子:
Input: N = 4, C = 15, V[]= {10, 10, 12, 18}, W[]= {2, 4, 6, 9}
Output:
Items taken into the knapsack are
1 1 0 1
Maximum profit is 38
Explanation:
1 in the output indicates that the item is included in the knapsack while 0 indicates that the item is excluded.
Since the maximum possible cost allowed is 15, the ways to select items are:
(1 1 0 1) -> Cost = 2 + 4 + 9 = 15, Profit = 10 + 10 + 18 = 38.
(0 0 1 1) -> Cost = 6 + 9 = 15, Profit = 12 + 18 = 30
(1 1 1 0) -> Cost = 2 + 4 + 6 = 12, Profit = 32
Hence, maximum profit possible within a cost of 15 is 38.
Input: N = 4, C = 21, V[]= {18, 20, 14, 18}, W[]= {6, 3, 5, 9}
Output:
Items taken into the knapsack are
1 1 0 1
Maximum profit is 56
Explanation:
Cost = 6 + 3 + 9 = 18
Profit = 18 + 20 + 18 = 56
方法:
在这篇文章中,讨论了对 0/1 背包问题使用最小成本(LC)的分支定界方法的实现。
可以使用FIFO 、 LIFO和LC策略解决分支限界问题。最低成本(LC)被认为是最智能的,因为它根据启发式成本函数选择下一个节点。它选择成本最低的那个。
由于0/1 Knapsack是关于最大化总值,我们不能直接使用LC Branch and Bound技术来解决这个问题。相反,我们通过取给定值的负数将其转换为最小化问题。
请按照以下步骤解决问题:
- 根据物品的价值/重量(V/W)比率对物品进行排序。
- 在优先级队列中插入一个虚拟节点。
- 重复以下步骤,直到优先级队列为空:
- 从优先级队列中提取 peek 元素并将其分配给当前节点。
- 如果当前节点的上限小于所有探索的节点的最小下限minLB ,则没有探索点。所以,继续下一个元素。不考虑上限大于minLB的节点的原因是,上限存储了可能达到的最佳值。如果最佳值本身不是最佳值minLB ,那么探索该路径是没有用的。
- 更新路径数组。
- 如果当前节点的级别为N ,则检查当前节点的下限是否小于finalLB ,即所有到达最终级别的路径的最小下限。如果为真,则更新finalPath和finalLB 。否则,继续下一个元素。
- 计算当前节点的右孩子的下界和上界。
- 如果当前项可以插入背包,则计算当前节点的左子节点的下界和上界。
- 更新minLB并在其上限小于minLB 时插入子项。
Illustration:
N = 4, C = 15, V[]= {10 10 12 18}, W[]= {2 4 6 9}
Left branch and right branch at ith level stores the maximum obtained including and excluding the ith element.
Below image shows the state of the priority queue after every step:
下面是上述方法的实现:
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 现场工作专业课程和学生竞争性编程现场课程。