Backpack problem-4. Multiple backpacks

Multiple knapsack problem

Title description

There are N items and a backpack with a capacity of V.

Item iThere are at most si items, The volume of each piece is vi, and the value is wi.

Solve which items are loaded into the backpack, so that the total volume of the items does not exceed the capacity of the backpack, and the total value is the largest. Output the maximum value.

Input format The
two integers in the first line, N, V, are separated by a space, which respectively indicate the number of objects and the volume of the backpack.

Next, there are N rows, each with three integers vi, wi, si, separated by spaces, indicating the volume, value, and quantity of the i-th item.

Output format
Output an integer, which represents the maximum value.

data range

Case1: 0<N,V≤100 ,0<vi,wi,si≤100
Case2: 0<N≤1000,0<V≤2000,0<vi,wi,si≤2000

Input sample

4 5
1 2 3 Volume value quantity
2 4 1
3 4 3
4 5 2

Sample output:

10

analysis:

Each item of a complete backpack can be used unlimited times, so there is only one restriction:, k*v<=Vthe number of choices k is limited by the volume of the
backpack ; there are s items in a multi-pack, so there are two restrictions,, k<=s && k*v<=Vk is limited by The number of the item itself.

The dynamic transfer equation satisfies:, dp[i][j]=max{dp[i-1][ j-k×v[i] ] + k×w[i]}where0 ≤ k×v[i] ≤ j && k<=s

When k<=sthe time but because of k*v<=Vthe time and exit, indicating that the article itself enough, the equivalent of complete knapsack problem,
but because when k>sexited, it indicates the number of items is too small, that is, multiple knapsack problem.
Now we consider the problem of multiple knapsacks. That is, the number s of each item is relatively small, and all the items can be packed into the backpack, that is s*v<=V, k is only limited by the number of items s:

If the number of items s is large enough, there will k*v<=Vbe there before s is reached , so there may be a case of exiting halfway.

Two-dimensional array implementation

#include <iostream>
#define read(x) scanf("%d",&x)
#define rep(i,a,b) for (int i=a;i<=b;i++)

using namespace std;

const int maxn=110,maxv=110;
int v[maxn],w[maxn],s[maxn];
int dp[maxn][maxv];

int main() {
    
    
    int N,V;
    read(N),read(V);
    rep(i,1,N) read(v[i]),read(w[i]),read(s[i]);

    rep(i,1,N)
        rep(j,1,V)
            for (int k=0;k<=s[i] && k*v[i]<=j;k++)
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);

    printf("%d",dp[N][V]);

    return 0;
}

However, this approach is more time-complex,Time complexity O (NVS), Only the data of Case1 can be used, and the data of Case2 will be TLE. If we optimize in the same way as the complete backpack, when the number of items s is enough, it is a complete backpack and can be optimized, but when the number of items When s is relatively small?

Assuming that the volume of the i-th item is v, the value is w, and there are s items, load it into the maximum value of j:
expand f[i,j]=MAX { f[i−1,j−k∗v]+k∗w },k=0,1,...,s as follows:

The premise of the above expansion formula is:, (s+1)*v<=jthat is s*v<=j-v, explanation: a backpack with a volume of j can hold s+1 pieces of this item, that is, a backpack with a volume of jv can hold s pieces of this item.

In this case, it cannot be optimized like a complete backpack: dp[i][j]=max(dp[i-1][j],dp[i][j-v]+w)
because no matter what kind of items in a complete backpack, such results can be optimized, and there are items in a complete backpack that can be optimized in this way, and there are also items that cannot be optimized in this way. , Not all meet.

Binary optimization

Core: Disassemble an item with a volume of v, a value of w, and a number of s into k items . These k items are completely equivalent, there is no difference between them, and there is no order problem.

Consider the binary number: 11111111, 8 ones, which represent the decimal number 255. By taking 0 or 1 from these 8 positions, all the numbers between 0 and 255 can be described.
These 8 1s represent 1, 2, 4, 8, 16, 32, 64, 128, that is, by selecting any of these 8 numbers, you can combine all the numbers between 0 and 255, select all It is 255, and if you don't select all, it is 0.

Therefore, for the decimal number N, it can be divided into 1, 2, 4, ..., 2 (k-1) , N-(2 k -1), Note: This is divided into k+1 numbers of
them Any combination between 0 and N can form all the numbers between 0 and N.

The first k numbers can make up any data between 0 and 2 k-1 -1 , plus N-(2 k -1) can make up any data between 0 and N.

Assuming s=200, then it can be broken down 200=1+2+4+8+16+32+64+73, and these values ​​are the weights and proportional coefficients.
Then you can disassemble this item with volume v, value w, and number s into 8 items:

v[1]=v, w[1]=w
v[2]=2v,w[2]=2w
v[3]=4v,w[3]=4w
v[4]=8v,w[4]=8w
v[5]=16v,w[5]=16w
v[6]=32v,w[6]=32w
v[7]=64v,w[7]=64w
v[8]=73v,w[8]=73w

The 8 newly split items are optional or not, and each item is split like this, so it turns into a 01 backpack problem.

In this way, for s items, it can be divided into log(s) to take the entire single item, and the time complexity ranges from O(NVS) to O(NVlogS).

Algorithm implementation

Note that the data range of Case2: 0<N≤1000, 0<V≤2000, 0<vi,wi,si≤2000
There are originally up to 1000 items, and the maximum volume of each item is 2000. For items with a volume of 2000, It can be divided into log(2000)+1 at most, so the actual number of items is the product of the two.

The maximum value of vi is 2000, log2000 is log(2×1000)=log2+log1000=1+3×log10,
log10 is greater than 3 and less than 4, so log2000 is greater than 10 and less than 13.

#include <iostream>
#define read(x) scanf("%d",&x)

using namespace std;

const int maxn=1010,maxv=2010;
int v[maxn*14],w[maxn*14];
int dp[maxv];              

int main()
{
    
    
    int N,V;
    read(N),read(V);
    //边输入边预处理,将每个物品进行拆分
    int a,b,s;
    int idx=0;  "idx指向数组的真实的最后一个位置"
    for (int i=1;i<=N;i++) {
    
    
        read(a),read(b),read(s);
        //进行拆分
        int k=1;
        while (k<=s) {
    
    
            v[++idx]=k*a,w[idx]=k*b;//拆分装入背包
            s-=k,k*=2;
        }
        if (s) v[++idx]=s*a,w[idx]=s*b;
        //不是2^k的话,最后还会剩下一个数
    }
    N=idx;//拆分后共有idx个物品
    for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    printf("%d",dp[V]);

    return 0;
}

It is also possible not to split first, and then process. You can split and process without additional maintenance of the array: knapsack problem-
the cost of mixed knapsack is that the number in the value w array is useless and does not have the meaning of input It was destroyed during reprocessing, and the result of the last split is recorded.

Monotonic queue optimization

pending upgrade.

Guess you like

Origin blog.csdn.net/HangHug_L/article/details/114259532