背包问题
目前已经学了三种背包问题,是时候整理一下模版了
01背包
这是最基础的背包问题,即每件物品只能取一次,问背包能装的不超过容量的最大价值
方程:dp[i][j]=max{dp[i-1][j],dp[i-1][j-w[i]]+val[i]}(j>=w[i])
边界:dp[i][j]=dp[i-1][j](j<w[i]),dp[0][j]=0;
空间优化:由于dp[i][]只与dp[i-1][]有关,所以可以用滚动数组或迭代成一维数组进行空间优化
滚动数组:让i%2即可,就不贴代码了
迭代转为一维数组:dp[j]=max(dp[j],dp[j-w[i]]+val[i]),此时j必须逆序遍历,关于为什么逆序在另一篇博客http://www.cnblogs.com/--560/p/4340764.html已经介绍的很清楚了,就不过多叙述了,这里只给代码
memset(dp,0,sizeof(dp)); for(int i=1;i<=N;i++){ for(int j=M;j>=0;j--){ if(j-w[i]>=0) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } /* 由于j>=w[i],所以上述代码也可以这样写 */ memset(dp,0,sizeof(dp)); for(int i=1;i<=N;i++){ for(int j=M;j>=w[i];j--){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } }
完全背包
即有n种物品,每种物品有无限个,还是问背包能装的不超过容量的最大价值
方程:dp[j]=max{dp[j-w[i]]+val[i],dp[j]} 方程和01背包表面上一样,但其实不一样,j必须顺序遍历;在另一篇博客已经解释的很清楚了,就不解释了,附链接http://www.cnblogs.com/--560/p/4345855.html
dp[0]=0; for(int i=1;i<=N;i++){ for(int j=M;j>=0;j--){ if(j-w[i]>=0) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } /* 同样也可以写成这样 */ dp[0]=0; for(int i=1;i<=N;i++){ for(int j=M;j>=w[i];j--){ dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } }
多重背包
即每种物品数量有限,问题同上
方法:用二进制优化并转为01背包,懒得写了,直接看下面的代码吧
void ZeroOnePack(int weight,int val) { for(int i=cash;i>=weight;i--){ dp[i]=max(dp[i],dp[i-weight]+val); } } void CompletePack(int weight,int val) { for(int i=weight;i<=cash;i++){ dp[i]=max(dp[i],dp[i-weight]+val); } } void MultiPack(int weight,int val,int amount) { if(weight*amount>=cash) CompletePack(weight,val);//如果超过限制,当完全背包处理 else{ //否则用二进制优化并转为01背包处理 int k=1; while(k<amount){//按k=1,2,4,...的顺序分解amount,对分解后的部分按01背包处理 ZeroOnePack(k*weight,k*val); amount-=k; k*=2; } ZeroOnePack(amount*weight,amount*val); //剩下部分也按01背包处理 } } int main() { while(cin>>cash>>N){ memset(dp,0,sizeof(dp)); for(int i=1;i<=N;i++){ cin>>n[i]>>D[i]; MultiPack(D[i],D[i],n[i]); } cout<<dp[cash]<<endl; } return 0; }