1.多重背包问题
问题描述:有一个容积为V的背包,同时有N种物品,有对应种类的体积w和价值v,且每种物品K件;求该背包最多能装下的物品价值总和。
分析问题:
将完全背包问题转换为0-1背包问题,背包对每种物品能装入min{k,V/wi}件,所以转换为0-1背包问题则物品总数N为
状态描述:dp[i][j]表示第i件物品对于当前占用容量为j的价值状态,其中1<=i<=N,0<=j<=V;
状态分析:第i件物品是否加入背包,
(1)加入,dp[i][j]等于dp[i-1][j-w]+v,第i-1件相对于j-wi的占容最大价值的最大价值加第i件价值;
(2) 没加入,d[i][j]等于dp[i-1][j],即与第i-1件到j占容的最大价值;
状态转换:
转换为一维:
转换结果:dp[V],第N件物品到占容为V的最大价值状态,即完全背包问题的最优价值总和。
时间复杂度分析:
测试数据集:
1
8 2
4 100 2
2 100 4状态矩阵:
0 0 0 100 100 100 100 100
0 0 0 100 100 100 100 200
0 100 100 100 100 200 200 200
0 100 100 200 200 200 200 300
0 100 100 200 200 300 300 300
0 100 100 200 200 300 300 400
Code(转换为0-1) :
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
int main(){
int C;
int V,N;
struct goods{
int w;
int v;
};
vector<int> dp(101,0);
vector<goods> goods(1,{0,0});//物品总数
int w,v,k;
cin>>C;
while(C--){
goods.erase(goods.begin()+1, goods.end());
cin>>V>>N;
int n=0;
for(int i=1;i<=N;i++){
cin>>w>>v>>k;
n+=min(k,V/w);
goods.insert(goods.end(), min(k,V/w), {w,v});
}
for(int j=0;j<=V;j++){
dp[j]=0;
}
for(int i=1;i<=n;i++){//物品最大总数N
for(int j=V;j>=goods[i].w;j--){//0-1,逆序
dp[j]=max(dp[j],dp[j-goods[i].w]+goods[i].v);
}
for(int j=1;j<=V;j++)
cout<<dp[j]<<" ";
cout<<endl;
}
cout<<dp[V]<<endl;
}
return 0;
}
简化0-1代码:
struct goodsClass{
int w;
int v;
int k;
}
for(int i=i;i<N;i++){//N为物品种类数
for(int j=V;j>goodsClass[i].w;j--){//自右向左,自左向右都可,逆序便于理解0-1背包
for(int k=1;k<goodsClass[i].k&&j>=k*goodsClass[i].w;k++){
dp[j]=max(dp[j],dp[j-k*goodsClass[i].w]+k*goodsClass[i].v);
}
}
}//求多重背包
以上简化时间复杂度为,若想优化其复杂度可使用二进制拆分法转换为以组为单位的0-1背包问题,其时间复杂度为,或是借助单调队列实现,其复杂度为.
*扩展
基于上述0-1简化代码,可实现对完全背包问题求解:
for(int i=1;i<=N;i++){
for(int j=V;j>=goodsClass[i].w;j--){
for(int k=1;j>=k*goodsClass[i].w;k++){//j>=k*goodsClass[i].w确保dp不越界,同时保证1<=k<=V/goodsClass[i].w
dp[j]=max(dp[j],dp[j-k*goodsClass[i].w]+k*goodsClass[i].v);
}
}
}
最后建议:求背包问题都转换为0-1背包问题,若需要优化则再考虑优化。