1.问题描述
问题描述: 有一个容量为V的背包,还有n个物体。现在忽略物体实际几何形状,我们认为只要背包的剩余容量大于等于物体体积,那就可以装进背包里。每个物体都有两个属性,即体积w和价值v。使物品装入背包的价值最大。
2.0-1背包问题的解决思路
1.方法一:枚举法,首先想到最简单的枚举法,通过列举所有结果,从中筛选出满足题意的结果。
2.方法二:回溯法,在枚举法的基础上进行约束剪枝和限界剪枝。
3.方法三:动态规划,运用动态规划思想,动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,使用递推算法解决一个个小问题,最终达到解决原问题的效果。
2.3.1、如果装不下当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。
2.3.2、如果装得下当前物品。
假设1 :装当前物品,在给当前物品预留了相应空间的情况下,前n-1个物品的最佳组合加上当前物品的价值就是总价值。
假设2:不装当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样
选取假设1和假设2中较大的价值,为当前最佳组合的价值。
3.问题求解中所遇到的问题及分析解决方案;
问题:枚举法和回溯法运算规模较大、时间较长,动态规划中存在重叠子问题,递归方程多次调用自身使时间复杂度较大,01背包问题无法用贪心算法来解决
解决方案:使用动态规划方法时利用备忘录方法解决重复计算的问题,用递推方程替代递归方程
4问题的解决方案
问题求解关键技术:约束剪枝、限界剪枝、备忘录方法
5.算法测试
在比较枚举法,回溯法,动态规划法完成问题的时间复杂度之后,发现动态规划法是该问题的最优解。通过对01背包问题解法的扩展,可解决完全背包、多重背包等一些问题。
枚举法:#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const int N=10;//全局变量,物品数量
const int bag=N;//全局变量,背包承重量
int max_value=0;//全局变量,记录能获得的最大价值
int backpack(int a[],int v[],int w[],int n,int t)
{
int max_v=0,max_w=0;//背包累积的最大价值和最大重量
if(t>n-1)//递归结束的条件
{
printf("找到一个方案");
for(int i=0;i<n;i++){//遍历数组a根据其记录的0、1值进行背包价值和重量计算
printf("%d",a[i]);
max_v=max_v+v[i]*a[i];//found!!!!!如何计算max_v
max_w=max_w+w[i]*a[i];//found!!!!!如何计算max_w
}
if(max_w>bag)//found!!!!!根据max_w判断该方案是否超重
printf("\n该方案总价%d,总重%d,超重!不可行!!!\n",max_v,max_w);
else{//如果不超重
if(max_v>max_value)//found!!!!!比较该方案所获得的价值是不是比全局最优解更优
max_value=max_v;//更新最优解
printf("\n该方案总价%d,总重%d,可行\n",max_v,max_w);
}
}else{
for(int i=1;i>=0;i--)//只取值1或0
{
a[t]=i;//记录当前物品选1或不选0
backpack(a,v,w,n,t+1);//递归到下一层
}
}
return max_value;//返回最大价值
}
int main()
{
int a[N],v[N],w[N],i,start,end;
printf("背包最大承重%d公斤\n",bag);
for(i=0;i<N;i++)//随机生成价值和重量
{
v[i]=rand()%5+1;
w[i]=rand()%5+1;
printf("物品%d,价值%d,重量%d\n",i,v[i],w[i]);
}
start=clock();
printf("能获得的最大价值为:%d\n",backpack(a,v,w,N,0));
end=clock();
printf("耗时:%dms\n",end-start);
return 0;
}
1-3枚举法运行结果
回溯法:
int backpack(int t,int now_v,int now_w)
{//t表示递归层数,now_v表示当前层所累积的价值,now_w表示当前层所累积的重量
int i;
if(t>N-1)//当递归到达叶子节点时
{
printf("找到一个方案");
for(i=0;i<N;i++)
{
printf("%d",a[i]);
}
if(now_w>bag)//判断方案是否超重
{
printf("\n该方案总价%d,总重%d,超重!不可行!!!\n",now_v,now_w);
}
else//如果不超重
{
if(now_v>max_value)//判断该方案所获得的价值是不是更优
{
max_value=now_v;//更新最优解
}
printf("\n该方案总价%d,总重%d,可行\n",now_v,now_w);
}
count++;
}
else
{
for(i=0;i<=1;i++)
{
a[t]=i;//记录当前物品选或不选
now_v=now_v+v[t]*i;//根据0-1值累加价值
now_w=now_w+w[t]*i;//根据0-1值累加重量
if(now_w<=bag&&now_v+r[t+1]>max_value) // (限件减枝 约束减枝)
backpack(t+1,now_v,now_w);//递归到下一层
}
}
return max_value;//返回最大价值
}
1-2回溯法运行结果
动态规划:
int jy[100][100]={0};//记忆数组
int backpack(int i,int m)//i为第i个物品,m为有m元钱
{
if(i == 0) return 0;//边界
if(jy[i][m]>0)return jy[i][m];
if(w[i]>m)
return jy[i][m]=backpack(i-1,m);//当这个物品装不下时 就不需要比较了
else
jy[i][m]=max(backpack(i-1,m),backpack(i-1, m - w[i])+v[i]);
return jy[i][m];
}
1-1动态规划运行结果图
分析算法及运行时间可知枚举法和回溯法的算法复杂度为O(2^n),而动态规划方法的算法复杂度为O(n)
6.总结
枚举法和回溯的思路较简单但是算法复杂度较高,动态规划的方法需要考虑的问题较多但是大大减低了算法复杂度
动态规划算法具有最优子结构性质,因此动态规划能得到最优解。01背包问题存在重叠子问题,利用备忘录方法避免重复计算相同子问题。文章来源:https://www.toymoban.com/news/detail-590976.html
注:贪心算法中,每步贪心决策都无法改变,而忽略了背包的容量,考虑的背包问题的局部最优,而不是全局最优。文章来源地址https://www.toymoban.com/news/detail-590976.html
到了这里,关于01背包问题的多种求解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!