当前位置:   article > 正文

回溯法-01背包问题之一:递归模式_01背包递归

01背包递归

一、回溯法

回溯法是一个既带有系统性又带有跳跃性的搜索算法。它在包含问题的所有解的解空间树中按照深度优先的策略,从根节点出发搜索解空间树。算法搜索至解空间树的任一节点时,总是先判断该节点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该节点为根的子树的系统搜索,逐层向其原先节点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。

运用回溯法解题通常包含以下三个步骤:

· 针对所给问题,定义问题的解空间;

· 确定易于搜索的解空间结构;

· 以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索;


二、01背包问题描述

01背包问题,即向容量为M的背包装载物品,要么放入要么不放入。从n个物品中选取装入背包的物品,物品i的重量为Wi,价值为Pi。最佳装载指装入的物品价值最高,即∑PiXi(i=1..n)取最大值。约束条件为∑WiXi≤M且Xi∈[0,1](1≤i≤n)。

在这个表达式中,需求出Xi的值。Xi=1表示物品i装入背包,Xi=0表示物品i不装入背包。

· 即判断可行解的约束条件是:∑WiXi≤M(i=0..n),Wi>0,Xi∈[0,1](1≤i≤n)

· 目标最大值:max∑PiXi(i=0..n-1),Pi>0,Xi=0或1(0≤i<n)

0/1背包问题是一个自己选取问题,适合于用子集树表示0/1背包问题的解空间。在搜索解空间树时,只要左儿子节点是一个可行节点,搜索就进入左子树,在右子树中有可能包含最优解才进入右子树搜索,否则将右子树剪去。


三、关于剪枝函数

设当前剩余物品价值总和为r,当前结点x价值为cp,当前x结点上界函数值为bp。L为当前已搜索到的答案结点中受益的最大值,当cp+r=bp<L时可剪去以X为根的子树。

计算右子树中解上界方法是将剩余物品按单位重量价值排序,一次放入物品直至装不下为止,再装入部分未装入物品直至装满背包,由此得到的价值是右子树解上界。

四、递归实现

如下图1所示为01背包问题递归实现的示意图,图2是01背包问题递归实现的流程图,描述了代码实现方案。


                                 

图1 01背包问题递归描述图                                     图2 01背包问题递归实现流程图


图1比较容易理解,是否已拿完物品也就是i<n(i是当前物品序号,n是物品总数目)是否成立,如果成立则递归结束并打印输出路径。如果i<n则判断第i个物品能否放入背包,如果不能放入,则考虑放置i+1个物品,如果能放入还存在当前第i个放入和不放入两种情形后对第i+1个的影响。注意在“放入还是不放入”的部分可考虑加入剪枝函数。

五、递归的代码实现


代码1 Main函数测试代码:

  1. public static void Main (string[] args)
  2. {
  3. Obj[] objs = new Obj[4];
  4. objs[0] = new Obj() { Weight = 7, Price = 42 };
  5. objs[1] = new Obj() { Weight = 3, Price = 12 };
  6. objs[2] = new Obj() { Weight = 4, Price = 40 };
  7. objs[3] = new Obj() { Weight = 5, Price = 25 };
  8. Backtracking_Recursion1 r = new Backtracking_Recursion1();
  9. r.W = 10;
  10. r.objs = objs;
  11. r.Backtracking(0);
  12. Console.Read();
  13. }

代码2Obj物品代码

  1. public class Obj
  2. {
  3. /// <summary>
  4. /// 物品重量
  5. /// </summary>
  6. public int Weight
  7. {
  8. get;
  9. set;
  10. }
  11. /// <summary>
  12. /// 物品价值
  13. /// </summary>
  14. public int Price
  15. {
  16. get;
  17. set;
  18. }
  19. /// <summary>
  20. /// 该物品是否放入包内
  21. /// </summary>
  22. internal bool Selected
  23. {
  24. get;
  25. set;
  26. }
  27. }


代码3递归实现01背包问题

  1. class Backtracking_Recursion1
  2. {
  3. #region field
  4. protected int m_currentWeight = 0;
  5. protected int m_currentPrice = 0;
  6. #endregion
  7. #region property
  8. /// <summary>
  9. /// 背包容量
  10. /// </summary>
  11. /// <value>默认20</value>
  12. public int W
  13. {
  14. get;
  15. set;
  16. }
  17. public int n
  18. {
  19. get
  20. {
  21. return objs == null ? 0 : objs.Length;
  22. }
  23. }
  24. /// <summary>
  25. /// 物品,包括重量/价值和数量
  26. /// </summary>
  27. /// <value>The objects.</value>
  28. public Obj[] objs
  29. {
  30. get;
  31. set;
  32. }
  33. #endregion
  34. public void Backtracking(int i)
  35. {
  36. if (i >= n)
  37. {
  38. Printing();
  39. return;
  40. }
  41. if (objs[i].Weight + m_currentWeight <= W)
  42. {
  43. m_currentWeight += objs[i].Weight;
  44. m_currentPrice += objs[i].Price;
  45. objs[i].Selected = true;
  46. Backtracking(i + 1);
  47. m_currentPrice -= objs[i].Price;
  48. m_currentWeight -= objs[i].Weight;
  49. }
  50. objs[i].Selected = false;
  51. Backtracking(i + 1);
  52. }
  53. /// <summary>
  54. /// 输出路径
  55. /// </summary>
  56. protected void Printing()
  57. {
  58. Console.Write("<");
  59. for (int i = 0; i < objs.Length; i++)
  60. {
  61. Console.Write(objs[i].Selected ? "1 " : "0 ");
  62. }
  63. Console.WriteLine(">, price: " + m_currentPrice.ToString()
  64. + "\t weight: " + m_currentWeight.ToString());
  65. }
  66. }



六运行结果


注:

1 代码3中Printing()函数调用后可判断并记录最优路径;

2 下文将讲述01背包问题回溯法的顺序执行方法,并通过模板模式整合两种不同的实现方案。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/333028
推荐阅读
相关标签
  

闽ICP备14008679号