我对于dp的使用仍然很不熟练,总结一下各种背包梳理一下。
01背包
1 void Knapsack_01(){ 2 memset(dp,0,sizeof(dp)); 3 for(int i=0;i<n;i++){ 4 for(int j=W;j>=w[i];j--){ 5 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 6 } 7 } 8 printf("%d ",dp[W]); 9 }
完全背包
1 void Knapsack_prefect(){ 2 memset(dp,0,sizeof(dp)); 3 for(int i=0;i<n;i++){ 4 for(int j=w[i];j<=W;j++){ 5 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 6 } 7 } 8 printf("%d ",dp[W]); 9 }
w很大的01背包
1 void BigW_01(){ 2 memset(dp,0x3f,sizeof(dp)); 3 dp[0]=0; 4 for(int i=0;i<n;i++){ 5 for(int j=MAX_N*MAX_V;j>=v[i];j--){ 6 dp[j]=min(dp[j],dp[j-v[i]]+w[i]); 7 } 8 } 9 int ans=0; 10 for(int i=0;i<=MAX_N*MAX_V;i++)if(dp[i]<=W)ans=i; 11 printf("%d ",ans); 12 }
w很大的完全背包
1 void BigW_prefect(){ 2 memset(dp,0x3f,sizeof(dp)); 3 dp[0]=0; 4 for(int i=0;i<n;i++){ 5 for(int j=v[i];j<=MAX_N*MAX_V;j++){ 6 dp[j]=min(dp[j],dp[j-v[i]]+w[i]); 7 } 8 } 9 int ans=0; 10 for(int i=0;i<=MAX_N*MAX_V;i++)if(dp[i]<=W)ans=i; 11 printf("%d ",ans); 12 }
多重背包
复杂度O(nWlog(m))。
算法思想是可以利用1,2,4,…,2k+a来表示一个数,因此,可以把m个相同物品看作是log(m)种不同的物品做01背包求解。
总之mi=1+2+4+…+a(0<=a<2k+1)
这样问题就转化为了nlog(m)个物品求01背包。
本题还有另一种复杂度为O(nW)的做法,利用了双端队列滑动最小值的思想,反正我是没看懂。之后看懂了补上。
1 void knapsack_multi(){ 2 memset(dp,0,sizeof(dp)); 3 for(int i=0;i<n;i++){ 4 int num=m[i]; 5 for(int k=1;num>0;k<<=1){ 6 int mul=min(k,num); 7 for(int j=W;j>=w[i]*mul;j--){ 8 dp[j]=max(dp[j],dp[j-w[i]*mul]+v[i]*mul); 9 } 10 num-=mul; 11 } 12 } 13 printf("%d ",dp[W]); 14 }