I. Dynamic Planning for solving the 0-1 knapsack problem
/*************************************** *********************************/
/* 0-1 backpack problems:
/* Given n items and a backpack
/* The Weight of item I is wi, and its value is vi.
/* The backpack capacity is C.
/* How should I select the items loaded into the backpack so that the items in the backpack can be selected?
/* Is the total value the largest?
/* Note: there are only two options for item I when you choose to attach a backpack,
/* Load or not load the backpack. You cannot load item I multiple times.
/* You cannot load only part of item I.
/*
/* 1. Formal Description of the 0-1 backpack problem:
/* Given C> 0, wi> 0, VI> 0, 0 <= I <= N, you must find a n-yuan
/* 0-1 vector (x1, x2,..., xn), making:
/* Max sum _ {I = 1 to n} (VI * XI), which meets the following constraints:
/* (1) sum _ {I = 1 to n} (WI * xi) <= C
/* (2) XI ε {0, 1}, 1 <= I <= N
/*
/* 2. Solution to the 0-1 backpack Problem
/* The 0-1 knapsack problem has the optimal sub-structure and subproblem overlapping nature, and is suitable
/* Use the dynamic planning method to solve the problem
/*
/* 2.1 optimal substructure
/* (Y1, Y2,..., yn) is an optimal solution for the 0-1 backpack problem.
/* Conclusion: (Y2, Y3,..., yn) is an optimal solution for the following subproblems:
/* Max sum _ {I = 2 to n} (VI * XI)
/* (1) sum _ {I = 2 to n} (WI * xi) <= C-W1 * Y1
/* (2) XI ε {0, 1}, 2 <= I <= N
/* If not, the subproblem has an optimal solution (Z2, Z3,..., Zn ),
/* (Y2, Y3,..., yn) is not the optimal solution. There are:
/* Sum _ {I = 2 to n} (VI * zi)> sum _ {I = 2 to n} (VI * Yi)
/* And W1 * Y1 + sum _ {I = 2 to n} (WI * zi) <= C
/* Further steps include:
/* V1 * Y1 + sum _ {I = 2 to n} (VI * zi)> sum _ {I = 1 to n} (VI * Yi)
/* W1 * Y1 + sum _ {I = 2 to n} (WI * zi) <= C
/* Note: (Y1, Z2, Z3,... Zn) is a better solution to the 0-1 backpack problem.
/* It indicates that (Y1, Y2,..., yn) is not the optimal solution of the problem. It is in conflict with the premise, so it is the best.
/* The sub-structure is established.
/*
/* 2.2 subproblem overlapping nature
/* Set the sub-problem P (I, j) of the 0-1 knapsack problem:
/* Max sum _ {k = I to n} (VK * XK)
/* (1) sum _ {k = I to n} (WK * XK) <= J
/* (2) XK ε {0, 1}, I <= k <= N
/* Problem P (I, j) is a subproblem when the backpack capacity is J and the optional items are I, I + 1,..., n.
/* Set M (I, j) to the optimal value of subproblem P (I, j), that is, the maximum total value. Based on the Optimum
/* Sub-structure, which can be used to establish a recursive M (I, j:
/* A. Recursive initial M (n, J)
/* // The size of the backpack is J, and the optional items are only n. If the size of the backpack J is greater than that of the item n
/* // Weight, it is loaded directly; otherwise, it cannot be loaded.
/* M (n, j) = Vn, j> = wn
/* M (n, j) = 0, 0 <= j <wn
/* B. Recursive M (I, j)
/* // The size of the backpack is J, and optional items are I, I + 1,..., n
/* // If the size of the backpack is j <WI, no item I is installed at all, so there are:
/* M (I, j) = M (I + 1, J), 0 <= j <wi
/* // If j> = WI, select between no-loaded item I and loaded item I.
/* The optimal value of unloaded item I: m (I + 1, J)
/* Optimal value of loaded item I: m (I + 1, J-WI) + vi
/* So:
/* M (I, j) = max {M (I + 1, J), m (I + 1, J-WI) + VI}, j> = wi
/*
/*************************************** *********************************/
# Define max (A, B) (a)> (B ))? (A): (B ))
# Define min (A, B) (a) <(B ))? (A): (B ))
Template <typename type>
Void knapsack (type * V, int * w, int C, int N, type ** m)
{
// Recursive Initial Condition
Int Jmax = min (W [N]-1, C );
For (Int J = 0; j <= Jmax; j ++ ){
M [N] [J] = 0;
}
For (j = W [N]; j <= C; j ++ ){
M [N] [J] = V [N];
}
// I ranges from 2 to n-1 to j> = WI and 0 <= j <wi even if M (I, j)
For (INT I = n-1; I> 1; I --){
Jmax = min (W [I]-1, C );
For (Int J = 0; j <= Jmax; j ++ ){
M [I] [J] = m [I + 1] [J];
}
For (j = W [I]; j <= C; j ++ ){
M [I] [J] = max (M [I + 1] [J], M [I + 1] [J-W [I] + V [I]);
}
}
M [1] [c] = m [2] [c];
If (C> = W [1]) {
M [1] [c] = max (M [1] [c], M [2] [C-W [1] + V [1]);
}
}
Template <typename type>
Void traceback (type ** M, int * w, int C, int N, int * X)
{
For (INT I = 1; I <n; I ++ ){
If (M [I] [c] = m [I + 1] [c]) x [I] = 0;
Else {
X [I] = 1;
C-= W [I];
}
}
X [N] = (M [N] [c])? 1:0;
}
Int main (INT argc, char * argv [])
{
Int n = 5;
Int W [6] = {-1, 2, 2, 6, 5, 4 };
Int V [6] = {-1, 6, 3, 5, 4, 6 };
Int c = 10;
Int ** ppm = new int * [n + 1];
For (INT I = 0; I <n + 1; I ++ ){
PPM [I] = new int [C + 1];
}
Int X [6];
Knapsack <int> (V, W, C, N, ppm );
Traceback <int> (ppm, W, C, n, x );
Return 0;
}
Ii. Greedy Algorithm for Solving the 0-1 knapsack problem
1. The basic idea of greedy method:
-- Starting from an initial solution of the problem, we gradually approach the given goal to obtain a better solution as quickly as possible. When a certain step in an algorithm is reached, the algorithm stops.
This algorithm has the following problems:
1). There is no guarantee that the final solution obtained is the best;
2). cannot be used to find the maximum or limit the problem;
3). Only feasible solutions that meet certain constraints can be obtained.
The process of implementing this algorithm:
Starting from an initial solution to the problem;
While can move one step forward to the given overall goal do
Find a solution element of the feasible solution;
Combine all solution elements into a feasible solution to the problem;
2. Example Analysis
1). [backpack problem] There is a backpack with a capacity of M = 150. There are 7 items, which can be divided into any size.
Make sure that the total value of the items in the backpack is the maximum, but the total capacity cannot be exceeded.
Item A B C D E F G
Weight 35 30 60 50 40 10 25
Value: 10 40 30 50 35 40 30
Analysis:
Target function: Maximum Σ pi
The constraint is that the total weight of the loaded item cannot exceed the size of the backpack: Σ wi <= m (M = 150)
(1) based on greedy strategies, is the best result of each selection of the most valuable items into a backpack?
(2) Can I find the optimal solution for loading the item with the smallest space?
(3) Each time you select the item with the largest unit capacity value, it becomes the solution strategy.
<Program code:> (Environment: C ++)
# Include <iostream. h>
# Define max 100 // maximum number of items
Void sort (int n, float a [Max], float B [Max]) // sort by value density
{
Int J, H, K;
Float T1, T2, T3, C [Max];
For (k = 1; k <= N; k ++)
C [k] = A [k]/B [k];
For (H = 1; H <n; H ++)
For (j = 1; j <= N-H; j ++)
If (C [J] <C [J + 1])
{T1 = A [J]; A [J] = A [J + 1]; A [J + 1] = T1;
T2 = B [J]; B [J] = B [J + 1]; B [J + 1] = t2;
T3 = C [J]; C [J] = C [J + 1]; C [J + 1] = T3;
}
}
Void knapsack (int n, float limitw, float V [Max], float W [Max], int X [Max])
{Float C1; // C1 indicates the remaining carrying capacity of the backpack.
Int I;
Sort (n, V, W); // sort items by value density
C1 = limitw;
For (I = 1; I <= N; I ++)
{
If (W [I]> C1) break;
X [I] = 1; // when X [I] is 1, item I is under solution.
C1 = c1-w [I];
}
}
Void main ()
{Int N, I, X [Max];
Float V [Max], W [Max], totalv = 0, totalw = 0, limitw;
Cout <"Enter N and limitw :";
Cin> N> limitw;
For (I = 1; I <= N; I ++)
X [I] = 0; // The item selection table is initialized to 0.
Cout <"Enter the value of an item in sequence:" <Endl;
For (I = 1; I <= N; I ++)
Cin> V [I];
Cout <Endl;
Cout <"Enter the item weight in sequence:" <Endl;
For (I = 1; I <= N; I ++)
Cin> W [I];
Cout <Endl;
Knapsack (n, limitw, V, W, X );
Cout <"the selection is :";
For (I = 1; I <= N; I ++)
{
Cout <X [I];
If (X [I] = 1)
Totalw = totalw + W [I];
}
Cout <Endl;
Cout <"total weight of the backpack:" <totalw <Endl; // total weight of the backpack
Cout <"Total value of a backpack:" <totalv <Endl; // total value of a backpack
}
Iii. Backtracking Algorithm for Solving the 0-1 knapsack problem
The 1.0-l knapsack problem is the subset selection problem.
Generally, the 0-1 knapsack problem is an NP problem. 0-1 backpack
The problem solution space can be represented by a subset tree. The Backtracking Method for Solving the 0-1 knapsack problem and the Backtracking Method for loading problems are very similar.
. When searching for a spatial tree, as long as its left son node is a feasible node, the search enters its left child tree. When
You can search for the right subtree only when the right subtree contains the optimal solution. Otherwise, cut the right subtree. Set R to the current residual
Total item values; CP is the current value; bestp is the current optimal value. When CP + R is less than or equal to bestp, you can cut the right
Subtree. A better way to calculate the upper bound of the solution in the right subtree is to sort the remaining items by their unit weight value, and then
Load an item in sequence until it cannot be loaded, then load a part of the item and fill it with a backpack. The resulting value is:
Upper bound of the solution in the right subtree.
2. solution:
In order to facilitate the calculation of the upper bound, you can first sort items by their unit weight value from large to small. After that, you only need to perform the test in order.
Check items. During implementation, bound calculates the upper bound of the current node. When searching for a spatial tree, as long as its left son node is a feasible node, the search enters the left child tree. In the right child tree, it may include the optimal solution before entering the right child tree search. Otherwise, cut the right subtree.
Backtracking is a systematic and leaping search algorithm. It searches for the solution space tree from the root node based on the depth-first policy in the solution space tree that contains all solutions to the problem. When the algorithm searches for any node of the spatial tree, it always determines whether the node certainly does not include the solution of the problem. If it is not included, the system searches for the subtree with the node as the root and traces back to its ancestor node layer by layer. Otherwise, go to the subtree and continue searching based on the depth-first policy. When the backtracking method is used to find all the solutions to the problem, it must be traced back to the root, and all the subtree of the root node has been searched. When the backtracking method is used to find any solution to the problem, it can end by searching for a solution to the problem. This kind of algorithm that systematically searches for a problem in depth-first mode is called backtracking, which is suitable for solving problems with a large number of combinations.
2. Algorithm Framework:
A. Space for solving the problem: the space for solving the problem should be clearly defined when the backtracking method is used. The solution space should be one (optimal) solution to the problem.
B. Basic Idea of the Backtracking Method: After determining the organizational structure of the space, the Backtracking Method starts from the Start Node (root node) and searches the entire solution space in depth-first mode. This Start Node becomes a dynamic node and an extension node. At the current extension node, search moves to a new node in depth. This new node becomes a new dynamic node and the current extended node. If you cannot move further at the current extension node, the current extension node becomes a dead end point. In other words, this node is no longer a dynamic node. In this case, you should move back to the nearest dynamic knot point and make it the current expansion node. The Backtracking Method recursively searches the solution space until the required solution or solution space has no dynamic knots.
3. Using backtracking to solve problems usually involves the following three steps:
A. Define the solution space for the given problem;
B. Determine a space structure that is easy to search;
C. Search space in depth-first mode, and use the pruning function in the search process to avoid invalid search;
# Include <iostream>
Using namespace STD;
Class knap
{
Friend int knapsack (int p [], int W [], int C, int N );
Public:
Void print ()
{
For (int m = 1; m <= N; m ++)
{
Cout <bestx [m] <"";
}
Cout <Endl;
};
PRIVATE:
Int bound (int I );
Void backtrack (int I );
Int C; // backpack capacity
Int N; // number of items
Int * w; // item weight array
Int * P; // array of item values
Int CW; // current weight
Int CP; // Current Value
Int bestp; // The current optimal value.
Int * bestx; // current Optimal Solution
Int * X; // current solution
};
Int knap: bound (int I)
{
// Calculate the upper bound
Int cleft = C-CW; // remaining capacity
Int B = CP;
// Load the item in descending order of the unit of weight and value of the item
While (I <= N & W [I] <= cleft)
{
Cleft-= W [I];
B + = P [I];
I ++;
}
// Full of backpacks
If (I <= N)
B + = P [I]/W [I] * cleft;
Return B;
}
Void knap: backtrack (int I)
{
If (I> N)
{
If (bestp <CP)
{
For (Int J = 1; j <= N; j ++)
Bestx [J] = x [J];
Bestp = CP;
}
Return;
}
If (CW + W [I] <= C) // SEARCH FOR THE LEFT subtree
{
X [I] = 1;
CW + = W [I];
CP + = P [I];
Backtrack (I + 1 );
CW-= W [I];
CP-= P [I];
}
If (bound (I + 1)> bestp) // search for the right subtree
{
X [I] = 0;
Backtrack (I + 1 );
}
}
Class Object
{
Friend int knapsack (int p [], int W [], int C, int N );
Public:
Int operator <= (Object A) const
{
Return (D> = A. D );
}
PRIVATE:
Int ID;
Float D;
};
Int knapsack (int p [], int W [], int C, int N)
{
// For knap: backtrack Initialization
Int W = 0;
Int p = 0;
Int I = 1;
Object * q = new object [N];
For (I = 1; I <= N; I ++)
{
Q [I-1]. ID = I;
Q [I-1]. d = 1.0 * P [I]/W [I];
P + = P [I];
W + = W [I];
}
If (W <= C)
Return P; // load all items
// Sort by item unit and weight
Float F;
For (I = 0; I <n; I ++)
For (Int J = I; j <n; j ++)
{
If (Q [I]. d <q [J]. d)
{
F = Q [I]. D;
Q [I]. d = Q [J]. D;
Q [J]. d = F;
}
}
Knap K;
K. P = new int [n + 1];
K. W = new int [n + 1];
K. x = new int [n + 1];
K. bestx = new int [n + 1];
K. X [0] = 0;
K. bestx [0] = 0;
For (I = 1; I <= N; I ++)
{
K. P [I] = P [Q [I-1]. ID];
K. W [I] = W [Q [I-1]. ID];
}
K. CP = 0;
K. CW = 0;
K. C = C;
K. N = N;
K. bestp = 0;
// Perform retrospective search
K. Backtrack (1 );
K. Print ();
Delete [] q;
Delete [] K. W;
Delete [] K. P;
Return K. bestp;
}
Void main ()
{
Int * P;
Int * W;
Int C = 0;
Int n = 0;
Int I = 0;
Char K;
Cout <"0-1 knapsack problem -- Backtracking Method" <Endl;
Cout <"by zbqplayer" <Endl;
While (k)
{
Cout <"Enter the backpack capacity (c):" <Endl;
Cin> C;
Cout <"Enter the number of items (n):" <Endl;
Cin> N;
P = new int [n + 1];
W = new int [n + 1];
P [0] = 0;
W [0] = 0;
Cout <"Enter the value of an item (P):" <Endl;
For (I = 1; I <= N; I ++)
Cin> P [I];
Cout <"Enter the weight of the item (w):" <Endl;
For (I = 1; I <= N; I ++)
Cin> W [I];
Cout <"optimal solution is (bestx):" <Endl;
Cout <"the optimal value is (bestp):" <Endl;
Cout <knapsack (p, W, C, N) <Endl;
Cout <"[s] restart" <Endl;
Cout <"[Q] Quit" <Endl;
Cin> K;
}
4. Solve the 0-1 knapsack problem using the branch restriction Method
1. problem description: it is known that there are n items and a backpack that can hold the weight of M. The weight of each item I is weight, and each item can only be put in or not, solving how to put the item, the total benefits of items in the backpack can be maximized.
2. design Philosophy and analysis: the selection of items constitutes a solution tree. The left subtree indicates not loading, the right indicates loading, and the optimal solution is obtained by searching the solution tree of the problem, use the upper bound of the node to kill nodes that do not meet the requirements.
# Include <iostream. h>
Struct good
{
Int weight;
Int benefit;
Int flag; // whether the flag can be loaded
};
Int number = 0; // number of items
Int upbound = 0;
Int CURP = 0, curw = 0; // current benefit value and weight
Int maxweight = 0;
Good * bag = NULL;
Void init_good ()
{
Bag = new good [number];
For (INT I = 0; I <number; I ++)
{
Cout <"Enter the item" <I + 1 <"Weight of the item :";
Cin> bag [I]. weight;
Cout <"Enter the item" <I + 1 <"item benefits :";
Cin> bag [I]. benefit;
Bag [I]. Flag = 0; // The initial flag is not loaded into the backpack.
Cout <Endl;
}
}
Int getbound (INT num, int * bound_u) // return the C and U limits of the current node.
{
For (int w = curw, P = CURP; num <number & (W + bag [num]. Weight) <= maxweight; num ++)
{
W = W + bag [num]. weight;
P = W + bag [num]. benefit;
}
* Bound_u = P + bag [num]. benefit;
Return (p + bag [num]. Benefit * (maxweight-W)/bag [num]. Weight ));
}
Void lcbag ()
{
Int bound_u = 0, bound_c = 0; // The C and U limits of the current node
For (INT I = 0; I <number; I ++) // traverse the solution tree layer by layer to determine whether to load each item.
{
If (bound_c = getbound (I + 1, & bound_u)> upbound) // traverse the left subtree
Upbound = bound_u; // change the existing U restriction without changing the flag.
If (getbound (I, & bound_u)> bound_c) // traverses the right subtree
// If it is loaded, determine whether the C limit of the right subtree is greater than the C limit of the Left subtree. If yes, load
{
Upbound = bound_u; // change the existing U limit
CURP = CURP + bag [I]. benefit;
Curw = curw + bag [I]. weight; // Add new item from existing weight and Benefit
Bag [I]. Flag = 1; // mark as loaded
}
}
}
Void display ()
{
Cout <"the number of items that can be placed in the backpack is :";
For (INT I = 0; I <number; I ++)
If (BAG [I]. Flag> 0)
Cout <I + 1 <"";
Cout <Endl;
Delete [] bag;
} Non