当前位置: 首页 > article >正文

背包问题之回溯法

问题描述:背包的容量为C,现有N件物品,价格分别为p[0],p[1]......p[n-1].重量分别为:w[0],w[1]......w[n-1].从N件物品中选择任意个放入背包中,使得物体的价值最大并且总重量不超过背包的容量C。

       采用数学语言描述如下:

      在  w[0]*x[0] + w[1] *x[1]+....... +w[n-1]*x[n-1]  < C, x[i] = 0 或1 的条件下

      求  p[0]*x[0] + p[1] *x[1]+....... +p[n-1]*x[n-1]  的最大值。

 

 

回溯法类其实也算枚举法的一种,但在搜索过程中,一般使用递归来完成。

 

回溯法的基本思想

对于用回溯法求解的问题,首先要将问题进行适当的转化,得出状态空间树。 这棵树的每条完整路径都代表了一种解的可能。通过深度优先搜索这棵树,枚举每种可能的解的情况;从而得出结果。但是,回溯法中通过构造约束函数,可以大大 提升程序效率,因为在深度优先搜索的过程中,不断的将每个解(并不一定是完整的,事实上这也就是构造约束函数的意义所在)与约束函数进行对照从而删除一些 不可能的解,这样就不必继续把解的剩余部分列出从而节省部分时间。

回溯法中,首先需要明确下面三个概念:

(一)约束函数:约束函数是根据题意定出的。通过描述合法解的一般特征用于去除不合法的解,从而避免继续搜索出这个不合法解的剩余部分。因此,约束函数是对于任何状态空间树上的节点都有效、等价的。

(二)状态空间树:刚刚已经提到,状态空间树是一个对所有解的图形描述。树上的每个子节点的解都只有一个部分与父节点不同。

(三)扩展节点、活结点、死结点:所谓扩展节点,就是当前正在求出它的子节点的节点,在DFS中,只允许有一个扩展节点。活结点就是通过与约束函数的对照,节点本身和其父节点均满足约束函数要求的节点;死结点反之。由此很容易知道死结点是不必求出其子节点的(没有意义)。

 

利用回溯法解题的具体步骤

首先,要通过读题完成下面三个步骤:

(1)描述解的形式,定义一个解空间,它包含问题的所有解。

(2)构造状态空间树。

(3)构造约束函数(用于杀死节点)。

 

然后就要通过DFS思想完成回溯,伪代码如下:

 

void BackTrack(int depth)

{

        if(depth > maxDepth) //已经到最大深度

        {

              if(solution is target)

                      save solution;

              return;

         }

         for (int i =0;i<TotalExpendNode;++i)

         {

               if(currentNode is searchable) //当前结点满足约束条件

               {

                      do something;

                      BackTrack(depth+1);

                      undo something;

                }

              

         }

         

}

 

 

对于背包问题其算法代码如下:

[c-sharp] view plain copy print ?
  1. #include <iostream>  
  2. #include <vector>   
  3.   
  4. using namespace std;  
  5.   
  6. class PackBackTrack  
  7. {  
  8.   
  9. protected:  
  10.     vector<int> m_p; //N个背包的价格  
  11.     vector<int> m_w; //N个背包的重量  
  12.     int         m_c; //背包的容量  
  13.     int         m_num; //物品的件数  
  14.     int         bestValue;          //背包最大价值  
  15.     int         currentValue;       //当前背包中物品的价值  
  16.     int         currentWeight;      //当前背包中物品的重量  
  17.   
  18. private:  
  19.     //辅助函数,用于回溯搜索   
  20.     void BackTrack(int depth)  
  21.     {  
  22.         if(depth >= m_num)    //达到最大深度  
  23.         {  
  24.             if(bestValue < currentValue)  //保存最优解  
  25.                 bestValue = currentValue;  
  26.             return ;  
  27.         }  
  28.   
  29.         if(currentWeight +m_w[depth] <= m_c)  //是否满足约束条件  
  30.         {  
  31.             currentWeight += m_w[depth];  
  32.             currentValue += m_p[depth];  
  33.               
  34.             //选取了第i件物品   
  35.             BackTrack(depth+1); //递归求解下一个结点  
  36.               
  37.             //恢复背包的容量和价值   
  38.             currentWeight -= m_w[depth];  
  39.             currentValue  -= m_p[depth];  
  40.         }  
  41.         //不取第i件物品   
  42.         BackTrack(depth+1);  
  43.     }  
  44.   
  45. public:  
  46.     //构造函数   
  47.     PackBackTrack();  
  48.     PackBackTrack(vector<int>& p,vector<int>& w, int c,int n)  
  49.         :m_p(p),m_w(w),m_c(c),m_num(n)  
  50.     {  
  51.         bestValue =0;  
  52.         currentValue =0;  
  53.         currentWeight =0;  
  54.     }  
  55.   
  56.     //获取背包内物品的最大值   
  57.     int GetBestValue()  
  58.     {  
  59.         BackTrack(0);  
  60.         return bestValue;  
  61.     }  
  62. };  
  63.   
  64.   
  65. int main(void)  
  66. {  
  67.     //测试程序   
  68.     int n;  
  69.     int c;  
  70.   
  71.     cout << "请输入物品的件数" << endl;  
  72.     cin >>n;  
  73.     cout << "请输入背包的容量" << endl;  
  74.     cin >>c;  
  75.     vector<int> w(n);  
  76.     vector<int> p(n);  
  77.   
  78.     cout << "请输入物品的重量:" << endl;  
  79.     for(int i=0;i<n;++i)  
  80.         cin >> w[i];  
  81.     cout << "请输入物品的价格:" << endl;  
  82.     for(int j=0;j<n;++j)  
  83.         cin >> p[j];  
  84.   
  85.     PackBackTrack pack(p,w,c,n);  
  86.   
  87.     int bestValue = pack.GetBestValue();  
  88.   
  89.     cout << "背包内的物品的最大价值为:" << bestValue << endl;  
  90.   
  91.     return 0;  
  92. }  

 

 

 

 

小结:回溯法作为一种穷举方法,可以使用约束函数来排除一些不可能的结点。虽然不理论上此算法在理论上最坏的情况下,复杂度仍为2^n,但在实际实验中,其搜索效率比上一次使用的2进制枚举要高很多。

http://www.lryc.cn/news/2420601.html

相关文章:

  • dell灵越笔记本后盖怎么拆_dell笔记本拆机详解【图文教程】
  • jQuery hover事件鼠标滑过图片半透明标题文字滑动显示隐藏
  • [ArcPy] 遥感影像去黑边-第六届全国大学生GIS技能大赛试题
  • 分享17个微信创新应用案例 应用场景的不同应用
  • DAVINCI DM3730开发攻略——xload-1.51移植
  • _desktop.ini病毒的清除
  • 开机启动项怎么设置
  • 「网络设备模拟器」EVE-NG安装操作指导
  • CRT常见故障问答
  • 实验二 基于MATLAB的离散时间系统的响应
  • 中国微电机行业需求规模与竞争格局研究报告2022版
  • google地图网页版_网站地图(Sitemap)的制作方法
  • Flex的itemRenderer属性使用例子
  • Internet Explorer 无法打开该 Internet 站点。请求的站点不可用
  • 在Python中如何使用模块进行代码组织
  • rootkit原理与编写教程
  • 中兴手机android版本升级包下载,刷机
  • 打造全新的Windows Live™ Spaces
  • AutoJs学习-天天爱消除脚本
  • 属兔的人今日运势-360星座网_【12月10日】 十二生肖明日运势
  • 属兔的人今日运势-360星座网_第一运程 2021年1月4日十二生肖运势解析
  • 如何制作优质的YouTube视频
  • Stream常用方法
  • 英语论文关于计算机网络,COMPUTER NETWORK 计算机网络(英语论文).doc
  • 联邦学习介绍
  • ospf动态路由协议——(最详解)
  • jQuery绑定事件的四种方式:bind、live、delegate、on
  • 三款企业必备企业上网监控软件盘点|上网行为监控软件有哪些
  • 用c语言实现二分法
  • pubg测试服服务器维护上不去,绝地求生测试服进不去怎么办 测试服上不去黑屏解决方法...