描述:ios
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包容量,且价值总和最大。数组
变式:有的物品能只有1个,有的物品有多个。less
状态转移方程:ide
dp[i][j]=max{dp[i][j],dp[i-1][j-c[i]*k]+w[i]*k}//0<=k<=n[i];优化
转化成01背包:(目的是便于下降空间复杂度)this
转化成2^n的模版spa
int total=p;//p是当前已知物品的数量 for(int i=1;i<=p;i++){ int s=1; while(n[i]>s){ total++; w[total]=w[i]*s; c[total]=c[i]*s n[i]=n[i]-s; s=s*2; } w[i]=n[i]*i; }
空间优化后的01背包模型code
//n:拆分合并后的物品的总数量 memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++){ for(int j=v;j>=c[i];j--){ dp[j]=max(dp[j],dp[j-c[i]]+w[i]); } } cout<<dp[v]<<endl; //输出了最大容量为v时能达到的最大价值
而后,谭小奎同志告诉了我一个优化的方法。blog
for i=1 to n if (c[i]*n[i]>v){ for j=0 to v //看成彻底背包来作 } else{ 二进制拆分红01背包 for(j=v to 0)//01背包 }
这样来讲,数据很好的话,时间复杂度大大下降了。谭小奎同窗告诉我彻底背包比01背包复杂度低不少,我才注意到时间的问题啊,之前一直没想过~~~~~ip
题型分析
参考01背包那篇博文中的描述。
一、多重背包的通常性解法,容量不定
Big Event in HDU
Description
Input
Output
Sample Input
Sample Output
这是一道典型的多重背包问题+性价比为一+装载无定值
背包容量是各类设备价值总和的一半。这里是能够化成01背包的,为体现多重背包的初始状态,贴上这个程序。
#include<iostream> #include<string.h> using namespace std; int dp[50+5][125000+5]; int w[55]; int nn[55]; int main(){ int n; while(cin>>n && n>=0){ if(n==0)cout<<"0"<<" "<<"0"<<endl; else{ int m=0; for(int i=1;i<=n;i++){ cin>>w[i]>>nn[i]; m+=w[i]*nn[i]; } int mm=m; m=m/2; memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=1;i<=n;i++){ for(int j=0;j<=nn[i];j++){ for(int k=m;k>=0;k--){ if(j*w[i]<=k && dp[i-1][k-j*w[i]]==1){ dp[i][k]=1; } } } } int ans=0; for(int i=m;i>=0;i--){ for(int j=n;j>=1;j--){ if(dp[j][i]==1) {ans=i;break;} } if (ans!=0) break; } cout<<(mm-ans)<<" "<<ans<<endl; } } return 0; }
二、2次拆分的01解法,容量不定
Dividing
Description
Input
Output
Sample Input
1 0 1 2 0 0 1 0 0 0 1 1 0 0 0 0 0 0
Sample Output
Collection #1: Can't be divided. Collection #2: Can be divided.
因为物品数不少,二维数组超空间,一一分配01作法超时间,咱们选择2次拆分来作。
拆分的模版在上面已经贴出了。接着按照01来作就能够了。
1 #include<iostream> 2 #include<string.h> 3 #define maxn 420000+5 4 using namespace std; 5 int dp[maxn]; 6 int w[100]; 7 int main(){ 8 int t=0; 9 while(1){ 10 t++; 11 int a[10]; 12 int m=0; 13 for(int i=1;i<=6;i++){ 14 cin>>a[i]; 15 m=m+i*a[i]; 16 } 17 if (m%2!=0) { 18 cout<<"Collection #"<<t<<":"<<endl; 19 cout<<"Can't be divided."<<endl; 20 cout<<endl; 21 continue; 22 }else 23 { 24 m=m/2; 25 if(a[2]+a[1]+a[3]+a[4]+a[5]+a[6]==0) break; 26 int total=6; 27 for(int i=1;i<=6;i++){ 28 int s=1; 29 while(a[i]>s){ 30 total++; 31 w[total]=i*s; 32 a[i]=a[i]-s; 33 s=s*2; 34 } 35 w[i]=a[i]*i; 36 }//如今已经转化成01背包 37 memset(dp,0,sizeof(dp)); 38 dp[0]=1; 39 for(int i=1;i<=total;i++){ 40 for(int j=m;j>=w[i];j--){ 41 if (dp[j-w[i]]) 42 dp[j]=1; 43 } 44 } 45 if(!dp[m]) { 46 cout<<"Collection #"<<t<<":"<<endl; 47 cout<<"Can't be divided."<<endl; 48 cout<<endl; 49 }else{ 50 cout<<"Collection #"<<t<<":"<<endl; 51 cout<<"Can be divided."<<endl; 52 cout<<endl; 53 } 54 } 55 } 56 return 0; 57 }