# Multiple knapsack problem

Got it. 01 knapsack as well as Full backpack Later. We continue to learn about multiple backpacks.

#### Restatement of problems

There are N items and a backpack with a capacity of V. The first item has si pieces at most, each of which has a volume of vi and a value of wi. Solving which items are put into the backpack can make the total volume of items not exceed the capacity of the backpack, and the total value is the largest. Output maximum value.

Input format

In the first line, two integers, N and V, are separated by spaces, indicating the number of items and the volume of the backpack.

Next, there are N lines. Each line is divided by three integers VI, WI and Si. They are separated by spaces to represent the volume, value and quantity of the i-th item.

Output format

Output an integer to represent the maximum value.

Analysis: compared with 01 knapsack and complete knapsack, multiple knapsack is between them. Each item has a clear quantity. We can't choose to take or not to take the item every time like 01 knapsack, nor can we choose to take or try every item when the volume of complete knapsack is enough. Let's understand three solutions according to the following three data ranges.

Data range 1: 0 < n, V ≤ 100; 0 < VI, WI, Si ≤ 100;

Data range 2: 0 < n ≤ 1000; 0 < V ≤ 2000; 0 < VI, WI, Si ≤ 2000;

Data range 3: 0 < n ≤ 1000; 0 < V ≤ 20000; 0 < VI, WI, Si ≤ 20000;

### Method 1 (violent solution):

Data range 1: 0 < n, V ≤ 100; 0 < VI, WI, Si ≤ 100;

Train of thought analysis: each item has its exact quantity. Compared with 01 backpack, our direct thinking is that we need to know how to choose or not to choose each item, and how many items to choose in the case of selection. In this way, its problem is the same as that of a complete backpack, but the difference is that we have our own upper limit of quantity. We can't take whatever we want under the limitation of volume like a complete backpack, and there is also the limitation of quantity. So we can only continue to judge on the basis of 01 backpack. The specific operation is to add another layer of circulation to continue to judge when each item, circulation volume.

The code is as follows:

#include<iostream> #include<vector> using namespace std; int main() { int N,V; vector<int>ans; cin>>N>>V; //Enter item type quantity and backpack volume ans.resize(V+10,0); //Open up a suitable size of one-dimensional array space to scroll and store the current answer for(int i=0;i<N;i++) //Start traversing items { int volume,value,counts; //The properties of the items entered on site are different from those of 01 knapsack. In addition to volume and value, there are more items cin>>volume>>value>>counts; for(int j=V;j>=volume;j--) //It's the same size as 01 backpack, from big to small { for(int k=1;k*volume<=j&&k<=counts;k++)//ans[j] does not select the current item itself. We use it to find the best one, and select several items in the case of selection {//The number of items that can be selected in the case of selection is from one to no more than the volume of the backpack and its own quantity ans[j]=max(ans[j],ans[j-k*volume]+k*value);//How much volume and value should be selected for state transition } } } cout<<ans[V]<<endl;//Output answer return 0; }

### Method 2 (binary method optimization):

Data range 2: 0 < n ≤ 1000; 0 < V ≤ 2000; 0 < VI, WI, Si ≤ 2000;

Train of thought analysis: to undertake method 1, we think that in the last method, for the innermost layer of circulation, what we compare is that in the case of legal quantity, there are 0, 1, 2, 3 items Choose or not. We are equivalent to spreading out the number of items, transforming them into 01 backpacks, and representing each possibility with different number combinations in 1 of counts. So all of these possibilities can be represented by fewer numbers than by different numbers of 1? So we have fewer cycles.

In fact, all numbers from 1 to counts can be represented by binary combination. For example, all numbers from 1 to 5 can be combined by: 1, 2, 2 (1, 2, 1 + 2, 2 + 2, 1 + 2 + 2). The specific reason is that if all the bits of a number are 1, then the combination of 1 on different bits can represent all the numbers from 1 to it. For example, if the binary of 7 is 111, then the combination of 001, 010, 100 (corresponding to 1, 2, 4 in decimal system) can represent all the numbers from 1 to 7. It's easier to understand. So the number from it to the next binary all 1 is the number of its numbers plus one less number. For example, the next binary with all 1 is 15 (1111). Then 11 is a number between 7 and 15. All numbers from 1 to 11 can be represented by 1, 2, 4, 4 (11-7); because 1, 2, 4 can represent 1 to 7, then adding 4 can represent 11. In this way, we can combine all the numbers from 1 to counts with the least number according to this principle without using only 1 combination.

The code is as follows:

#include<iostream> #include<vector> using namespace std; struct Node{ //Define the structure and rebuild 01 knapsack with it int volume; int value; }; int main() { int N,V; vector<int>ans; vector<Node>Goods; //Define structure array cin>>N>>V; //Enter number of items and backpack size ans.resize(V+10,0); //Open up a suitable size of one-dimensional array space to scroll and store the current answer for(int i=0;i<N;i++) { int volume,value,counts; cin>>volume>>value>>counts;//Volume, value and quantity of on-site input items for(int j=1;j<=counts;j*=2)//Start to disassemble quantity and rebuild the value and volume of commodities { counts-=j; Goods.push_back({j*volume,j*value}); } if(counts>0) //If there is surplus, build a product separately { Goods.push_back({counts*volume,counts*value}); } } for(int i=0;i<Goods.size();i++)//Next is the familiar 01 backpack. Go through each item { for(int j=V;j>=Goods[i].volume;j--)//From big to small { ans[j]=max(ans[j],ans[j-Goods[i].volume]+Goods[i].value);//state transition } } cout<<ans[V]<<endl;//Output answer return 0; }

### Method 3 (monotone queue method optimization):

Data range 3: 0 < n ≤ 1000; 0 < V ≤ 20000; 0 < VI, WI, Si ≤ 20000;

We need to understand first The principle of monotone queue Then we continue to carry on Method 1 in combination with 01 knapsack. We find that when the volume, value and quantity of an item's attributes are input, the actual volume we want to traverse actually becomes 0 to min (counts * volume, 5) While traversing these volumes, we judge all the possibilities that each volume can hold the current goods. In fact, we can find that these volumes can be divided into volume groups (the remainder is from 0 to volume-1) according to different volume remainder. For each group, the volume between them is related.

For example, 2, 2 * volume+2, 3 * volume+2, 4 * volume+2 In fact, the optimal solutions of these volumes are all transformed from the previous comparison. First, we have made all the comparisons, In fact, we can use the principle of monotone queue to make the queue head always store the previous legal optimal solution (here, the legal mainly means that the number should not exceed counts), so that when we traverse to this volume, we only need to compare the unselected and the legal optimal solution (that is, the queue head element) under the condition of selection In this way, we can only traverse the volume once with one layer less circulation.

The code is as follows:

#include<iostream> #include<cstring> #define Max 20010 using namespace std; int ans[Max];//Define ans array as 01 knapsack to scroll and store answers int temp[Max]; /*Unlike 01 knapsack, we can't update ans directly because we need to jump to update the answer. In order to avoid data loss So we define another temp array, copy the contents of ans every time, and then update the ans array*/ int deq[Max]; //Define the volume of the maximum value within the legal range of the two terminal queue head's permanent storage int N,V; int main() { cin>>N>>V; //Enter item quantity and backpack volume for( int i = 0; i < N; i++ )//Start traversing items { memcpy(temp,ans,sizeof(ans));//Copy the current answer in ans before you start int volume, value,counts; cin>> volume >> value >> counts;//Volume, value and quantity of on-site input items for(int j=0; j<volume; j++) //Traverse different remainder of current volume { int front=0,back=-1; //Initialization queue empty for(int k = j; k <= V; k+=volume) //Optimization by monotone queue for traversing a volume with the same remainder when the volume does not exceed the limit { if(front<=back && ( k-deq[front] ) / volume > counts) {//Judge if the number of items needed for the best volume exceeds the given number when the queue is not empty, it is not legal for the first team to leave front++; } if( front <= back ) {//If the queue is not empty and the best volume combination rule allows the best volume to compare with the current state, select the maximum state transition ans[k] = max( ans[k],temp[deq[front]] + ( k-deq[front] ) / volume * value ); } while(front <= back && temp[deq[back]] + ( k-deq[back] ) / volume * value < temp[k] ) {//Keep the end of the queue out when the queue is not empty and the value of the current volume is always greater than the end of the queue element back--; } deq[++back]=k;//Finally, let the current volume enter the queue. If the above judgment meets the conditions, this step must be executed } } } cout<<ans[V];//Output answer return 0; }