多重背包问题(bounded knapsack)

问题:有N个种物品和一个容量为V的背包,第i种物品有n[i]件可用(有限件数),每件物品的重量是w[i],价值是c[i],求将哪些物品装入背包中,总重量不超过背包的容量,但价值总和最大?

我们可以把某多个物品数量拆分成每一件,按01背包问题处理。


当某物品数量非常多的时候,拆分成01问题,会造成DP表特别大。


将任意物品数量转化成2进制,分成类,再按01背包问题处理


代码实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**************************************************************** 
 * Description: C++ implementation of multiKnapsack 
 * Author: Alex Li
 * Date: 2023-06-20 10:53:09
 * LastEditTime: 2023-06-20 12:39:18
****************************************************************/
#include <iostream>
using namespace std;

const int MAXN=10000;//定义最大物品数量
const int MAXV=10000;//定义最大背包容量
int N; //物品数量
int V; //背包容量
int Weight[MAXN];  //存储每件物品的重量
int Value[MAXV];   //存储每件物品的价值
int dp[MAXV];      //滚动数组


//滚动数组解决01背包问题
void knapsack(int n){
    for(int i=0;i<=V;i++)dp[i]=0;
    for (int i = 1; i <=n; i++){
        for(int j=V; j>=Weight[i];j--)
           dp[j]=max(dp[j-Weight[i]]+Value[i],dp[j]);
    }
}

int main(){
    //实际物品的重量、价值和数量
    int realWeight[MAXN],realValue[MAXV],realNumber[MAXN];
    int k=0; //k是储存物品展开后的标号
    cin>>N>>V; //输入原始物品数量和背包容量。
    //输入N个物品的重量、价值和数量
    for(int i=1;i<=N;i++)cin>>realWeight[i]>>realValue[i]>>realNumber[i];
    
    /*把物品数量按二进制展开成多个物品
    比如A物品有20个,每个物品价值是3元,展开成如下:
    物品   数量   价值
    A1     1      3
    A2     2      6
    A3     4     12
    A4     8     24
    A5     5     15
    */
    
    for (int i =1; i <=N; i++){
        for (int j =1; j <=realNumber[i]; j<<=1){
            k++;
            Weight[k]=realWeight[i]*j;
            Value[k]=realValue[i]*j;
            realNumber[i]-=j;
        }
        if(realNumber[i]!=0){
        k++;
        Weight[k]=realWeight[i]*realNumber[i];
        Value[k]=realValue[i]*realNumber[i];
        }    
    }
    //经过二进制展开后,物品数量为k,对应每个物品的重量Weight[i]、价值Value[i]
    knapsack(k);
    cout<<dp[V];
}
Scroll to Top