题目描述:有N种物品,每种物品的数量为C1,C2......Cn。从中任选若干件放在容量为W的背包里,每种物品的体积为W1,W2......Wn(Wi为整数),与之相对应的价值为P1,P2......Pn(Pi为整数)。求背包能够容纳的最大价值。
3 6 2 2 5 3 3 8 1 4 1
输出示例
9
分析:本题是多重背包问题,我们显然可以把每种物品的每一件都作为一个新的物品按照普通0-1背包的方法做。
但是0-1背包的时间复杂度是O(W * N) , 这里N = C1 + C2 + …+ Cn。
如果用dp[i][j]表示前i件物品,总重量为j的时候的最大价值。那么dp[i][j] = max{dp[i – 1][j – k * Wi] + k * Vi},其中 0 ≤ k ≤ min( j / Wi , Ci),这个的时间复杂度是n * W * max(Ci)。
其实我们还是应该将问题转化为0-1背包的方法解决,利用二进制的方法。每个数都可以拆分为2的次方,这样通过将数量进行2的次方拆分,得到新的重量和价值的物品。
二进制思想:
任何整数都可以表示为多个二进制数相加。
假如说现在某种物品有三件,那么dp的时候,要算三次,分别为:放一件的时候,放两件的时候,放三件的时候,,,要三次循环,何不知,在放两件的时候,可能出现第一件已经放入的情况了,这时候其实是放了三件,等到三个一起放的时候,可能出现已经放过一件或两件或三件了。。所以一定要用二进制方法
定理:一个正整数n可以被分解成1,2,4,…,2^(k-1),n-2^k+1(k是满足n-2^k+1>0的最大整数)的形式,且1~n之内的所有整数均可以唯一表示成1,2,4,…,2^(k-1),n-2^k+1中某几个数的和的形式。
样例:
2 2 5 3 3 8 1 4 1
转化成01后:
2 2 4 4 4 4 3 3 6 6 12 12 3 3 1 4
代码:
#include<iostream> #include<cstdio> #include<cstring> #define Max(a,b)a>b?a:b using namespace std; int dp[50005]; int weigth[1005],value[1005]; int main() { int n,v; scanf("%d%d",&n,&v); int sum=0; int x,y,z; for(int i=0;i<n;i++) { scanf("%d%d%d",&x,&y,&z); int temp=1; while(z>0) { if(z>=temp) { weigth[++sum]=temp*x; value[sum]=temp*y; z=z-temp; } else { weigth[++sum]=z*x; value[sum]=y*z; z=0; } temp=temp*2; } } memset(dp,0,sizeof(dp)); for(int i=1;i<=sum;i++) { for(int j=v;j>=weigth[i];j--) { // if(j>=weigth[i]) dp[j]=Max(dp[j],dp[j-weigth[i]]+value[i]); } } printf("%d\n",dp[v]); return 0; }