今天看了一天的01背包 总结下今天所学的内容 我又来水博客了
首先01背包是属于dp中背包类型中的一种
01背包是指 一些东西只有选和不选的两种状态 对应于0和1
在这之前 说一下dp的两种特性无后效性,子问题重叠性质和最优化原理
无后效性 子问题一旦确定就不会在改变 不会因为后面更大的问题而改变子问题
子问题重叠性质动归对于递归的优化 递归产生的新问题并不总是新问题 有些子问题重复计算 动归就针对这点将结果保存在一个表格中 以获得更高的效率
最优化原理保证问题及其子问题的解是最优的
那么直接上例子
问题
有一个背包(容器)能装c大小(容量)的物品, 现有n件物品每件物品拥有它自己的质量w和价值v,问在不超过背包大小时能获得的最大价值是多少
假定 背包c为10 物品数量n为4 且分别的wi为1,3,4,6 。vi为2,3,5,4
那么首先分析递归方法
对于每件物品只有拿和不拿的状态
那么就会如图的分下去
直到最后得出每种情况 并选出最大值是10 也就是装123的情况
模型归纳
通过分治法 首先将问题抽象至背包中物品的选择视为yn 既求max(yx1v1 + yx2v2 … + ynmaxvmax)(yx1v1 + yx2v2 … + ynmaxvmax<c)
假定有函数f(i,j)表示背包剩余容量为j,前i个物品最佳组合 那么会出现两种情况:
1.背包容量可以装下物品
2.背包容量装不下物品
第一种情况下此时需要判断是否是装了这个物品价值会增加 这种情况下f(i-1,j-wi)+vi 而另一种是不增加总价值 就是f(i-1,j)
第二种情况下此时背包的最优价值与i-1个物品价值一样及f(i,j)=f(i-1,j)
综上 递推式子为
f(i,j)=f(i-1,j)
f(i,j)=max(f(i-1,j),f(i-1,j-wi)+vi)
那么可以得出递归代码如下
#include<iostream>
#include<cmath>
using namespace std;
int v[]={0,1,3,4,6},w[]={0,2,3,5,4},n,m;
int f(int i,int c){
int res=0;
if(i==0||c==0)
res=0;
else if(w[i]>c)
res=f(i-1,c);
else{
res=max(f(i-1,c),f(i-1,c-w[i])+v[i]);
}
return res;
}
int main(){
int result=f(4,10);
cout<<result<<endl;
return 0;
}
我们可以发现递归要将所有情况都试一遍
说完递归的方法 先来看看dp的代码
#include<iostream>
#include<cmath>
using namespace std;
int v[]={0,1,3,4,6},w[]={0,2,3,5,4},n,m,num[5][11];
int main(){
for(int i=0;i<5;i++)
num[0][i]=0;
for(int i=0;i<11;i++)
num[i][0]=0;
for(int i=1;i<5;i++)
for(int j=1;j<11;j++)
if(j<w[i])num[i][j]=num[i-1][j];
else num[i][j]=max(num[i-1][j],num[i-1][j-w[i]]+v[i]);
cout<<num[4][10]<<endl;
}
我们会发现多了一个num的二维数组来存储
存储在里面的数值分别是如图
稍加修改既能套用简单的01背包,之后可能会水几篇01背包问题的博客(咕咕咕)总之算法博大精深 要学的还很多 那么加油吧 奥里给!