描述: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 }