回溯法—-0-1背包问题

回溯法—-0-1背包问题算法描述 0 1 背包问题是子集选取问题 一般情况下 0 1 背包问题是 NP 完全问题 0 1 背包问题的解空间可以用子集树表示 解 0 1 背包问题的回溯法与解装载问题的回溯法十分相似 在搜索解空间树时 只要其左儿子节点是一个可行的节点 搜索就进入其左子树 而当右子树中有可能包含最优解时才进入右子树搜索 否则将右子树剪去 设 r 是当前剩余物品价值总和 cp 是当前价值 bestp 是当前最优价值 当 cp r lt bestp 时 可剪去右子树 计算右子树中解的上界的更好的办法是 将剩余物品依其单位重量价值排序 然后依

[算法描述]

0-1背包问题是子集选取问题。一般情况下,0-1背包问题是NP完全问题。0-1背包问题的解空间可以用子集树表示。解0-1背包问题的回溯法与解装载问题的回溯法十分相似。在搜索解空间树时,只要其左儿子节点是一个可行的节点,搜索就进入其左子树;而当右子树中有可能包含最优解时才进入右子树搜索否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r<=bestp时,可剪去右子树。计算右子树中解的上界的更好的办法是,将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包,由此得到的价值是右子树的上界

0–1背包的一个实例n=5, c=10, w={2, 2, 6, 5, 4}, v(p)={6, 3, 5, 4, 6}的0-1背包问题的最优解和最优值。

[回溯法基本思想]

确定了解空间的组织结构后,【回溯法从开始节点(根节点)出发,以深度优先搜索整个解空间。这个开始的节点为活节点,同时成为当前的扩展节点。在当前的扩展节点处,搜素向纵深方向移至一个新节点。这个新节点就成为新的活节点,并成为当前扩展节点。如果当前节点处不能再向纵深方向移动,则当前扩展节点为死节点。此时,应往回移动到最近的一个活节点处。回溯法以这种方式递归的在解空间中搜素,直至找到所有符合要求的解或解空间中已无活节点。】(即深度优先搜素)

【优化方法】

剪枝(一):当前决策放入的物品总重量已经大于背包容量时,没有必要继续决策,此时可以将其左子树剪掉。

剪枝(二):如果将当前扩展节点后剩下的所有物品都装入还没有目前已求得的最优值大的话,就不在进行决策了,直接返回。

递归回溯时,在当前扩展节点处会通过设置约束函数限界函数。不满足条件的,剪去相应的子树

【0-1背包算法分析】

对于0-1背包问题,可用一颗完全二叉树表示其解空间,针对上述实例(n=5),解空间树有32个可能的解,解空间树如下图所示。

回溯法----0-1背包问题回溯法----0-1背包问题回溯法----0-1背包问题 经剪枝(二)(红框框)先按照单位价值对物品进行从达到下排序。然后按照背包问题计算当前节点的上界仅当进入右子树时才计算,以判断是否可将右子树剪去进入左子树时不需要计算上界,因为其上界与其父节点相同

回溯法----0-1背包问题

回溯法----0-1背包问题当前节点的上界 详细计算如下图所示:

回溯法----0-1背包问题

这点理解很重要(区别于动态规划)

【0-1背包代码描述】

template 
  
    class Knap{ friend Typep Knapsack(Typep*,Typew*,Typew,int); private: Typep Bound(int i); void Backtrace(int i); Typew c;// 背包容量 int n;// 物品数 Typew *W;// 物品重量数数组 Typep *p;// 物品价值数组 Typew cw;// 当前重量 Typep cp;// 当前价值 Typep bestp;// 当前最优值 }; template 
   
     void Knap 
    
      ::Backtrace(int i){ if(i > n){ //到达叶子结点 bestp=cp; return; } if(cw + w[i] <= c){ // 进入左子树 cw += w[i]; cp += p[i]; Backtrace(i+1); cw -= w[i]; cp -= p[i]; } if(Bound(i+1) > bestp) // 进入右子树 Backtrace(i+1); } template 
     
       Typep Knap 
      
        ::Bound(int i){ // 计算上界 Typew cleft = c - cw; // 剩余容量 Typep b = cp; while(i <= n&&w[i] <= cleft){ // 以物品单位重量价值递减装入物品 cleft -= w[i]; b += p[i]; i++; } if(i <= n) // 装满背包 b += p[i]*cleft/w[i]; return b; } class Object { friend int Knapsack(int *,int *,int ,int); public: int operator<=(Object a) const{ return(d >= a.d); } private: int ID; float d; }; template 
       
         Typep Knapsack(Typep p[],Typew w[],Typew c,int n){ // 为Knap::Backtrace初始化 Typew W = 0; Typep P = 0; Object *Q = new Object[n]; for(int i=1;i<=n;i++){ Q[i-1].ID = i; Q[i-1].d = 1.0*p[i]/w[i]; P += p[i]; W += w[i]; } if(W <= c) return P; // 装入所有物品 Sort(Q,n); // 按物品单位重量价值排序(从大到小) Knap 
        
          K; K.p = new Typep[n+1]; K.w = new Typew[n+1]; for(int i=1;i 
          
         
        
       
      
     
    
  

【实例算法(害,偷个懒)】

// n=5, c=10, w={2, 2, 6, 5, 4}, v={6, 3, 5, 4, 6}的0-1背包问题的最优解和最优值。 #include 
  
    using namespace std; #define N 10 int w[N];//重量 int v[N];//价值 int x[N];//1表放入背包,0表不放入 int n,c;//n:物品个数 c:背包的最大容量 int cw=0;//当前物品总重 int cv=0;//当前物品总价值 int bestp=0;//当前最大价值 int bestx[N];//最优解 //回溯函数 k表示当前处在第几层做选择,k=1时表示决定是否将第一个物品放入背包 void backtrack(int k) {//叶子节点,输出结果 if(k>n){//找到一个更优的解 if(cv>bestp){//保存更优的值和解 bestp = cv; for(int i=1; i<=n; i++) bestx[i] = x[i]; } } else{//遍历当前节点的子节点 for(int i=0; i<=1; i++){ x[k]=i; if(i==0){ backtrack(k+1); } else{//约束条件:当前物品是否放的下 if((cw+w[k])<=c){ cw += w[k]; cv += v[k]; backtrack(k+1); cw -= w[k]; cv -= v[k]; } } } } } int main() { cout<<"请输入物品的个数:"; cin>>n; cout<<"请输入每个物品的重量及价值:"< 
   
     >w[i]>>v[i]; } cout<<"请输入背包的限制容量:"; cin>>c; backtrack(1); cout<<"最优值是:"< 
     
    
  

结果:

回溯法----0-1背包问题

 【时间和空间复杂度分析】

计算上界Bound()函数需要O(n)时间,在最坏的情况下有O(2^n)个右子节点需要计算上界,所以解0-1背包的回溯算法法BackTrack需要的计算时间为O(n2^n)实际上,最坏情况下,剪枝并不会改变时间复杂度。个人感觉0-1背包还是动态规划算法比较好吧。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/176605.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午9:31
下一篇 2026年3月26日 下午9:31


相关推荐

  • Mysql中用SQL增加、删除字段,修改字段名、字段类型、注释,调整字段顺序总结

    Mysql中用SQL增加、删除字段,修改字段名、字段类型、注释,调整字段顺序总结1.增加一个字段 代码如下 复制代码 //增加一个字段,默认为空altertableuseraddCOLUMNnew1VARCHAR(20)DEFAULTNULL; //增加一个字段,默认不能为空altertableuseraddCOLUMNnew2VARCHAR(20)NOTNULL; 2….

    2022年6月1日
    56
  • 关于java的垃圾回收机制,下面哪些结论_java垃圾回收算法有哪些

    关于java的垃圾回收机制,下面哪些结论_java垃圾回收算法有哪些本篇文章介绍了Java的垃圾回收机制、引用类型、JVM一次完整的GC流程、垃圾回收算法以及经典的垃圾回收器

    2022年8月31日
    5
  • 操作系统思维导图—(零基础—思维导图详细版本及知识点)

    操作系统思维导图—(零基础—思维导图详细版本及知识点)第一章操作系统引论及概述 1 操作系统 OperatingSys OS 是计算机系统中最重要的系统软件 它管理整个计算机系统的软件资源和硬件资源 是用户与计算机硬件的桥梁 是其它软件和程序的运行基础 2 操作系统可以控制 CPU 的工作 访问存储器 进行设备驱动和设备中断处理 3 计算机用户可以通过操作系统使用不同的界面 方便 快捷 安全 可靠地操作计算机硬件来完成自己的计算任务 4 操作系统是管理和控制计算机硬件与软件资源的计算机程序 是直接运行在 裸机 上的最基本的系统软件 任何其他软

    2026年3月18日
    3
  • 计算机房安全防范措施,机房安全防护方案「建议收藏」

    计算机房安全防范措施,机房安全防护方案「建议收藏」《机房安全防护方案》由会员分享,可在线阅读,更多相关《机房安全防护方案(3页珍藏版)》请在人人文库网上搜索。1、呜踏坡仁因亏遮寞蜂返脉刑罚发娜滨砖恶躺虞议蛤闹猪品钧捉绸瞎蜒囤运盐燥赁羚岩解锁周趁潞钵脏熬菜锹霉议保色觉汹刺茁恶即领递块聘协渠段波痒曾作滓率炙溯档蹬赃创竞屡环柬萝吏鸥帜竿胸耳蝎撇茁验婆州跑沦绥惯捉察歹洪妒蜒比侣血葫轴并蒸司咱惜狙窒茬畅揣痊潘拳帆巫眉思工拥跟矣申兑龙华卑氧躇峦恢奠业千朽孩荫…

    2022年7月12日
    17
  • 人脸对齐:DCNN级联关键点检测2013

    人脸对齐:DCNN级联关键点检测2013近期学习关键点检测相关内容,基于CNN的方法已经替代以往经典的方法(ASM,AAM等),于是乎得看看CNN是如何应用到关键点检测上的。创新点: 1.将CNN应用到人脸关键点检测当中 2.提出级联CNN,这个级联CNN的level-1有一个非常重要的作用,就是解决了传统人脸关键点检测时都会遇到的一个问题——关键点初始化,传统参数化方法(ASM,AAM等)若初始化不当,容易陷入局部最优。 虽然作者没…

    2022年5月6日
    48
  • 【Android 】零基础到飞升 | ExpandableListView(可折叠列表)的基本使用

    【Android 】零基础到飞升 | ExpandableListView(可折叠列表)的基本使用2.5.5ExpandableListView(可折叠列表)的基本使用本节引言:本节要讲解的Adapter类控件是ExpandableListView,就是可折叠的列表,它是ListView的子类,在ListView的基础上它把应用中的列表项分为几组,每组里又可包含多个列表项。至于样子,类似于QQ联系人列表,他的用法与ListView非常相似,只是ExpandableListVivew显示的列表项需由ExpandableAdapter提供。下面我们来学习这个控件的基本使用!官方API:Exp

    2022年6月15日
    23

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号