首先,什么叫多重背包呢?
大概意思就是:一个背包有V总容量,有N种物品,其价值分别为Val1,Val2……,Val3,体积对应的是Vol1,Vol2,……,Vol3,件数对应Num1,Num2……,Num3
求背包至多装满的最大总价值。
其实,首先可以当作01背包来做,这时候复杂度就是O(V*sum(Num[i]));
简单代码来说就是
for(int i=1;i<=N;i++) for(int j=1;j<=Num[i];j++) for(int k=V;k>=Vol[i];k++) dp[k]=max(dp[k],dp[k-Vol[i]]+Val[i]);
很容易超时。如果M大的话。从大神的背包九讲看到一个化成O(Vsum(log(Num[i])))
大概意思就是利用先判断是否为完全背包情况,否则二进制压缩进行01背包的步骤。任何一个数目Num[i],可以表示为1+2^1 +……+2^p+Num[i]-2^(p+1)+1 其中确定p的条件是Num[i]-2^(p+1)+1>0,Num[i]-2^(p+2)+1<0
这样我们就可以将第i件物品化成 logNum[i]件物品,起对应的容量和价值,分别对应相应倍数
简单代码就是
int i,j,k; for(i=1; i<=N; i++) if(Vol[i]*Num[i]>=V) //n*v>=V 完全背包 { for(j=Vol[i]; j<=V; j++) //从val到V { dp[j]=max(dp[j],dp[j-Vol[i]]+Val[i]); } } else { k=1; int num_tmp=Num[i]; //取出数目 while(k<num_tmp) //二进制优化 { for(j=V; j>=k*Vol[i]; j--) //k*Vol ,k*Val的背包 dp[j]=max(dp[j],dp[j-k*Vol[i]]+Val[i]); num_tmp-=k; k<<=1; } for(j=V; j>=num_tmp*vol[i]; j--) //num_tmp*Vol[i],num_tmp*Val[i]背包问题 { dp[j]=max(dp[j],dp[j-num_tmp*Vol[i] ]+Val[i]); } }之后对于HDU1171,2194,用第一种算法就轻松通过了。
HDU1171的题目要我们做的就是将总和/2规划成资产“不会大”的学院当成容量背包。
代码
#include<stdio.h> #include<string.h> int dp[250000]; int val[52]; int num[52]; int main(){ int n,i,j,k; int sum,V; while(scanf("%d",&n)&&n>0){ memset(dp,0,sizeof(dp)); sum=0; for(i=1;i<=n;i++){ scanf("%d%d",&val[i],&num[i]); sum+=val[i]*num[i]; } V=sum/2; //B的 for(i=1;i<=n;i++){ for(j=1;j<=num[i];j++){ for(k=V;k>=val[i];k--){ dp[k]=dp[k]>dp[k-val[i]]+val[i]?dp[k]:dp[k-val[i]]+val[i]; } } } printf("%d %d\n",sum-dp[V],dp[V]); } return 0; }
#include<stdio.h> #include<string.h> int dp[106]; int cst[100]; int val[100]; int num[100]; int max(int a,int b){ return a>b?a:b; } int main(){ int cases,V,N,i,j,k; scanf("%d",&cases); while(cases--){ scanf("%d%d",&V,&N); memset(dp,0,sizeof(dp)); memset(cst,0,sizeof(cst)); memset(val,0,sizeof(val)); memset(num,0,sizeof(num)); for(i=1;i<=N;i++) scanf("%d%d%d",&cst[i],&val[i],&num[i]); for(i=1;i<=N;i++) for(j=1;j<=num[i];j++) for(k=V;k>=cst[i];k--){ dp[k]=max(dp[k],dp[k-cst[i]]+val[i]); } printf("%d\n",dp[V]); } return 0; }
大概思路就是:令vol=val,来做成背包,能付多少钱嘛,那么dp[i]==i就是刚好付i元,最后统计一下就好啦。
一开始我试着直接化成01背包,果断TLE了……
/*******************************************************************************/ /* OS : 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 UTC 2013 GNU/Linux * Compiler : GCC 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) * Encoding : UTF8 * Date : 2014-03-11 * All Rights Reserved by yaolong. *****************************************************************************/ /* Description: *************************************************************** *****************************************************************************/ /* Analysis: ****************************************************************** *****************************************************************************/ /*****************************************************************************/ #include<stdio.h> #include<string.h> int dp[100002]; int val[122]; int num[122]; int max(int a,int b) { return a>b?a:b; } int main() { int V,N,i,j,res; while(~scanf("%d%d",&N,&V)&&(N||V)) { memset(dp,0,sizeof(dp)); for(i=1; i<=N; i++) scanf("%d",&val[i]); for(i=1; i<=N; i++) scanf("%d",&num[i]); for(i=1; i<=N; i++) { if(val[i]*num[i]>=V) //n*v>=V 完全背包 { for(j=val[i]; j<=V; j++) //从val到V { dp[j]=max(dp[j],dp[j-val[i]]+val[i]); } } else { int k=1; int num_tmp=num[i]; //取出数目 while(k<num_tmp) //二进制优化 { for(j=V; j>=k*val[i]; j--) //k*val ,k*val的背包 dp[j]=max(dp[j],dp[j-k*val[i]]+k*val[i]); num_tmp-=k; k<<=1; } for(j=V; j>=num_tmp*val[i]; j--) //num_tmp*val[i],num_tmp*val[i]背包问题 { dp[j]=max(dp[j],dp[j-num_tmp*val[i]]+num_tmp*val[i]); } } } res=0; for(i = 1; i<=V; i++) if(dp[i]==i) { res++; } printf("%d\n",res); } return 0; }
后来我又写了一遍,因为我当初把各种背包函数写在外面出错了,其实我一直不明白为什么GCC提交,打注释就有问题……
/*******************************************************************************/ /* OS : 3.2.0-58-generic #88-Ubuntu SMP Tue Dec 3 UTC 2013 GNU/Linux * Compiler : GCC 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) * Encoding : UTF8 * Date : 2014-03-11 * All Rights Reserved by yaolong. *****************************************************************************/ /* Description: *************************************************************** *****************************************************************************/ /* Analysis: ****************************************************************** *****************************************************************************/ /*****************************************************************************/ #include<stdio.h> #include<string.h> int dp[100002]; int w[122]; //weight int n[122]; //num int v[122]; //volume int V,N;//Volume of the Pack,Number of things int max(int a,int b) { return a>b?a:b; } void ZeroOnePack(int vol,int weight){ int i; for(i=V;i>=vol;i--){ dp[i]=max(dp[i],dp[i-vol]+weight); } } void CompletePack(int vol,int weight){ int i; for(i=vol;i<=V;i++){ dp[i]=max(dp[i],dp[i-vol]+weight); } } void MultiPack(int vol,int weight,int num){ if(num*vol>=V){//CompletePack CompletePack(vol,weight); return ; } int k=1; while(k<num){ ZeroOnePack(k*vol,k*weight); //ZeroOnePackage num-=k; k<<=1; } ZeroOnePack(num*vol,num*weight); } int main() { int i,res; while(scanf("%d%d",&N,&V),N+V){ memset(dp,0,sizeof(dp)); for(i=1;i<=N;i++) scanf("%d",&w[i]); for(i=1;i<=N;i++) scanf("%d",&n[i]); for(i=1;i<=N;i++) MultiPack(w[i],w[i],n[i]); res=0; for(i=1;i<=V;i++) if(dp[i]==i){ res++; } printf("%d\n",res); } return 0; }