处理何种问题:给定n种物品和一个容量为V的背包,物品i的体积为vi,其价值为pi,以及数量numi,求其最终可以装进背包的物品最大价值。
性能:时间复杂度为O(nV)
原理:客观上来说,可以将其直接看做01背包(只不过相同的物品很多),但会因为物品数量太多造成超时。针对这个问题,我没将物品数量进行分段处理,例如面对一个体积为6,价值为4,数量为10的物品,可以将其转化为一下这几个物品:
数量为1 |
体积为6 |
价值为4 |
数量为2 |
体积为6*2 |
价值为4*2 |
数量为4 |
体积为6*4 |
价值为4*4 |
数量为3 |
体积为6*3 |
价值为4*3 |
这些数量的物品可以组成1~10任意数量的物品。
以此再将其套用01背包,进行处理。
进行分段时的规律是1,2,4,8,16,32,64……()还有一段凑不够下一段的余数。
实现步骤:先对数量进行分段+01背包
备注:不要忘记那一段凑不够的余数,有了那段余数才能保证1~num的任意数都可以组成。
输入样例解释:
3 6 //n、V
2 2 5//第一种物品的体积、价值、数量
3 3 8
1 4 1
输出样例解释:
9 //所能构成的最大价值
#include<iostream> #include<cstdio> #include<string.h> using namespace std; const int MaxN=100010; struct node { int p,v; }; node arr[MaxN]; int dp[MaxN]; int N,V; int main() { int ctor,k,v,p,num; while(~scanf("%d%d",&N,&V)) { ctor=1; for(int i=1; i<=N; ++i) { scanf("%d%d%d",&v,&p,&num);//将 num 拆成:1,2,4,8,16......这样无论如何都能组成想要的物品大小,又可以避免直接转化为01背包造成的物品太多 k=1; while(num>k) { num-=k; arr[ctor].v=k*v; arr[ctor].p=k*p; k*=2; ++ctor; } arr[ctor].v=num*v; arr[ctor].p=num*p; ++ctor; } memset(dp,0,sizeof(dp)); for(int i=1; i<ctor; ++i) { for(int j=V; j>=arr[i].v; --j) { dp[j]=max(dp[j],dp[j-arr[i].v]+arr[i].p); } } printf("%d ",dp[V]); } return 0; }