0-1背包问题代码
一、什么是01背包问题?
举个例子,你要去一个水果摊拿水果,每种水果都有对应的两种属性:占用的体积V和蕴含的价值W。而你的背包体积为N。老板说:每种水果只能拿一个!因此对于咱们肯定得想一种搭配方式使得拿的水果总体积不超过背包容积,但是价值总和达到最大!
核心思想:
f[i][j]:表示所有选法集合中,只从前i个物品中选,并且总体积不大于j的选法的集合,它的值是这个集合中每一个选法的最大值。
对于01背包问题选择方法的集合可以分成2种:
①不选第i个物品,并且总体积不大于j的集合所达到的最大值:f[i-1][j]
②选择1~i个物品,并且总体积不大于j的集合所达到的最大值f[i][j]
对于第二种情况我们很难计算,因此需要思考从另一个角度解决问题。当选择1~i个物品,总体积不大于j的集合的最大值可以转化成选择1~i-1个物品,总体积不大于j-V[i]的集合+最后一个物品的价值:f[i-1][j-V[i]]+w[i]
因此总结:f[i][j]= Max{f[i-1][j],f[i-1][j-v[i]]+w[i]}!!!
二、01背包例题
输入样例
4 5 1 2 2 4 3 4 4 5
输出样例
8
三、二维数组代码实现
#include <iostream> using namespace std; const int N=1010; int v[N],w[N],f[N][N]; int n,m; int max(int a ,int b){ return a>b?a:b; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&v[i],&w[i]); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { f[i][j]=f[i-1][j]; if(j>=v[i]) { f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]); } } printf("%d",f[n][m]); return 0; }
##注意 :为什么i和j从1开始遍历,因为如果i或j不管哪个为0,f[i][j]其实都等于0!!
三、一维数组代码优化
如何优化:
从二维做法中可以看出f[i] [j]最大值的更新只用到了 f[i-1] [j],即 f[i-2][j] 到 f[0][j] 是没有用的。
所以第二层循环可以直接从v[i] 开始。
for (int i = 1; i <= n; i++) { for (int j = v[i]; j <= m; j++) { f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); } }
二维优化到一维后:
如果删掉f[i]这一维,结果如下:如果j层循环时递增的,则是错误的
for (int i = 1; i <= n; i++) { for (int j = v[i]; j <= m; j++) { f[j] = max(f[j], f[j - v[i]] + w[i]); } }
#include <iostream> using namespace std; const int N=1010; int v[N],w[N],f[N]; int n,m;
int max(int a ,int b){
return a>b?a:b;
}
int main() { scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d",&v[i],&w[i]);
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
printf("%d",f[m]);
return 0;
}