01背包&完全背包
Posted s-gloria
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了01背包&完全背包相关的知识,希望对你有一定的参考价值。
·01背包&完全背包基础
01背包模型:给定n个物品,第i个物品体积为Wi,价值为Vi,背包容量为sum,选择一些物品放入背包,要求总价值最大。
F[i,j]表示前i个物品放入容量为j的包里获得的最大价值。
对于任意一个物品都有两种状态,要么放要么不放,不放的话很显然价值同前,放的话就要从包里拿出一部分体积。
完全背包模型:给定n种物品,第i个物品体积为Wi,价值为Vi,背包容量为sum,选择一些物品放入背包,要求总价值最大。
F[i,j]表示前i种物品放入容量为j的包里获得的最大价值。
01背包方程:f[i,j]=max(f[i-1,j],f[i-1,j-Wi]+V[i]) (if j>=Wi)
完全背包方程:f[i,j]=max(f[i,j],f[i-1,j-Wi]+V[i]) (if j>=Wi)
目标:max{f[N][j](0<=j<=sum)}
1 #include<iostream> 2 using namespace std; 3 int n,sum; 4 int f[100][11000]; 5 int w[1100],v[1100]; 6 int main() 7 { 8 cin>>n>>sum; 9 for(int i=1;i<=n;i++) 10 cin>>w[i]>>v[i]; 11 f[0][0]=0; 12 for(int i=1;i<=n;i++) 13 for(int j=0;j<=sum;j++) 14 { 15 if(j>=w[i])f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]); 16 else f[i][j]=f[i-1][j]; 17 } 18 cout<<f[n][sum]; 19 return 0; 20 }
由于01背包中每一阶段的i状态只与上一阶段的i-1有关,于是就可以用滚动数组降低空间开销啦~
在所有f__的第一维里全部都加上&,使dp状态从f[0]_和f[1]_中交替。
1 #include<iostream> 2 using namespace std; 3 int n,sum; 4 int f[3][11000]; 5 int w[1100],v[1100]; 6 int main() 7 { 8 cin>>sum>>n; 9 for(int i=1;i<=n;i++) 10 cin>>w[i]>>v[i]; 11 f[0][0]=0; 12 for(int i=1;i<=n;i++) 13 for(int j=0;j<=sum;j++) 14 { 15 if(j>=w[i])f[i&1][j]=max(f[(i-1)&1][j],f[(i-1)&1][j-w[i]]+v[i]); 16 else f[i&1][j]=f[(i-1)&1][j]; 17 } 18 cout<<f[n&1][sum]; 19 return 0; 20 }
写到这里也很明确了,F数组的第一维完全可以省略,则F[j]表示背包中放入体积为j的物品的最大价值。
强调01背包的倒叙循环,完全背包的正序循环,因为01背包的转移是从i-1->i,而完全背包是i之间的转移。
循环到j时:
1.F数组后半段 f[j..m]处于第i个状态,已考虑过i
2.F前半段f[0...j-1]处于i-1个状态,未考虑过i
则j不断减小就意味着从i-1->i状态的转移。
(还可以初始化f为0,代表未放入物品时价值为0)
1 #include<iostream> 2 using namespace std; 3 int n,sum; 4 int f[11000]; 5 int w[1100],v[1100]; 6 int main() 7 { 8 cin>>n>>sum; 9 for(int i=1;i<=n;i++) 10 cin>>w[i]>>v[i]; 11 for(int i=1;i<=n;i++) 12 for(int j=sum;j>=w[i];j--) 13 f[j]=max(f[j],f[j-w[i]]+v[i]); 14 cout<<f[sum]; 15 return 0; 16 }
1 #include<iostream> 2 using namespace std; 3 int n,sum; 4 int f[11000]; 5 int w[1100],v[1100]; 6 int main() 7 { 8 cin>>n>>sum; 9 for(int i=1;i<=n;i++) 10 cin>>w[i]>>v[i]; 11 for(int i=1;i<=n;i++) 12 for(int j=w[i];j<=sum;j++) 13 f[j]=max(f[j],f[j-w[i]]+v[i]); 14 cout<<f[sum]; 15 return 0; 16 }
·01背包应用
栗1:给定n个正整数,从中选出若干个使它们和为sum,求方案数。
f[j]表示和为j的方案数有多少
1 #include<iostream> 2 #define MAXN 1100 3 using namespace std; 4 int f[MAXN],a[MAXN]; 5 int main() 6 { 7 int n,sum; 8 cin>>n>>sum; 9 for(int i=1;i<=n;i++) 10 cin>>a[i]; 11 f[0]=1; 12 for(int i=1;i<=n;i++) 13 for(int j=sum;j>=a[i];j--) 14 f[j]+=f[j-a[i]]; 15 cout<<f[sum]; 16 }
栗2:给定n个物品,体积是Vi,质量为Wi,价值为Ki,给定体积为SUM,负载量为M的背包,在体积载量允许的情况下求最大价值。
还是简单的01背包,不过是体积多个维度而已;
1 #include<iostream> 2 using namespace std; 3 int v[51],w[51],k[51],f[401][401]; 4 int main() 5 { int n,sum,m; 6 cin>>n>>sum>>m; 7 for(int i=1;i<=n;i++) 8 cin>>v[i]>>w[i]>>k[i]; 9 for(int i=1;i<=n;i++) 10 for(int j=sum;j>=v[i];j--) 11 for(int l=m;l>=w[i];l--) 12 f[j][l]=max(f[j][l],f[j-w[i]][l-w[i]]+k[i]); 13 cout<<f[sum][m]; 14 return 0; 15 }
·完全背包应用
给定自然数n,把n拆分成几个正整数相加的形式,数可以重复使用,求方案数。
相当于n个物体,体积分别是1,2,3..n,背包容量为n,每个物品可重复使用,用板子就好了,求和类似于上面数字组合。
1 #include<iostream> 2 using namespace std; 3 int f[1100]; 4 int main() 5 { 6 int n; 7 cin>>n; 8 f[0]=1; 9 for(int i=1;i<=n;i++) 10 for(int j=1;j<=n;j++) 11 f[j]=f[j]+f[j-1]; 12 cout<<f[n]; 13 }
以上是关于01背包&完全背包的主要内容,如果未能解决你的问题,请参考以下文章
Juice 完全背包 & Bone Collector 0-1背包问题