完全背包
有n种重量和价值分别为wi,vi的物品,从这些物品中挑选总重量不超过W的物品,求出挑选物品价值总和的最大值。在这里,每种物品可以挑选任意多件。
限制条件:1<=n<=100,1<=wi,vi<=100,1<=W<=10000。
分析一下:dp[i+1][j]代表从前i种物品中挑选总重量不超过j时总价值的最大值。
递推关系为:dp[0][j]=0;
dp[i+1][j]=max{dp[i][j-k*w[i]]+k*v[i] | 0<=k}
可以写出程序如下
1 #include<iostream> 2 #include<algorithm> 3 #include<memory.h> 4 using namespace std; 5 const int max_n=101; 6 int w[max_n+1]; 7 int v[max_n+1]; 8 int n,r,W; 9 int main() 10 { 11 cin>>n; 12 for(int i=0;i<n;i++) 13 { 14 cin>>w[i]>>v[i]; 15 } 16 cin>>W; 17 int dp[n+1][W+1]; 18 memset(dp,0,sizeof(dp)); 19 for(int i=0;i<n;i++) 20 { 21 for(int j=0;j<=W;j++) 22 { 23 for(int k=0;k*w[i]<=j;k++) 24 { 25 dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]); 26 } 27 } 28 } 29 cout<<dp[n][W]<<endl; 30 return 0; 31 }
关于k的循环最坏可能从0循环到W,所以这个算法的复杂度为O(nW*W)
可以做变形:max{dp[i][j-k*w[i]]+k*v[i] | 0<=k}
=max(dp[i][j],max{dp[i][j-k*w[i]]+k*v[i] | 1<=k})
=max(dp[i][j],max{dp[i][(j-w[i])-k*w[i]]+k*v[i] | 0<=k}+v[i])
=max(dp[i][j],dp[i+1][j-w[i]]+v[i])
所以递推关系式
dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])
我们和01背包的递推关系式比较一下
dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])
复杂度降为O(nW)
程序如下
1 #include<iostream> 2 #include<algorithm> 3 #include<memory.h> 4 using namespace std; 5 const int max_n=101; 6 int w[max_n+1]; 7 int v[max_n+1]; 8 int n,r,W; 9 int main() 10 { 11 cin>>n; 12 for(int i=0;i<n;i++) 13 { 14 cin>>w[i]>>v[i]; 15 } 16 cin>>W; 17 int dp[n+1][W+1]; 18 memset(dp,0,sizeof(dp)); 19 for(int i=0;i<n;i++) 20 { 21 for(int j=0;j<=W;j++) 22 { 23 if(j>=w[i]) 24 { 25 dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]); 26 } 27 else 28 { 29 dp[i+1][j]=dp[i][j]; 30 } 31 } 32 } 33 cout<<dp[n][W]<<endl; 34 return 0; 35 }
也可以通过过重复利用一个数组来实现dp。这个比较不好理解一些。
看01背包的情况
1 #include<iostream> 2 #include<algorithm> 3 #include<memory.h> 4 using namespace std; 5 const int max_n=101; 6 int w[max_n+1]; 7 int v[max_n+1]; 8 int n,r,W; 9 int main() 10 { 11 cin>>n; 12 for(int i=0;i<n;i++) 13 { 14 cin>>w[i]>>v[i]; 15 } 16 cin>>W; 17 int dp[W+1]; 18 memset(dp,0,sizeof(dp)); 19 for(int i=0;i<n;i++) 20 { 21 for(int j=W;j>=w[i];j--) 22 { 23 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 24 } 25 } 26 cout<<dp[W]<<endl; 27 return 0; 28 }
看完全背包的情况
1 #include<iostream> 2 #include<algorithm> 3 #include<memory.h> 4 using namespace std; 5 const int max_n=101; 6 int w[max_n+1]; 7 int v[max_n+1]; 8 int n,r,W; 9 int main() 10 { 11 cin>>n; 12 for(int i=0;i<n;i++) 13 { 14 cin>>w[i]>>v[i]; 15 } 16 cin>>W; 17 int dp[W+1]; 18 memset(dp,0,sizeof(dp)); 19 for(int i=0;i<n;i++) 20 { 21 for(int j=w[i];j<=W;j++) 22 { 23 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 24 } 25 } 26 cout<<dp[W]<<endl; 27 return 0; 28 }
两者的差异只有循环的方向