n+1 回溯法n后问题解空间树实战使用求助

 
(1)回溯法n后问题解空间树描述
n个重量分别为{w1w2wn}的物品,它们的价值分别为{v1v2vn},给定一个容量为W的背包设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中要求选中的物品不仅能够放到背包中,而重量和为W有最大的价值
输入:
3 // n个物品假设为3
16 45 // 第一个粅品的重量和价值
15 25 // 第二个物品的重量和价值
15 25 // 第三个物品的重量和价值
30 // 背包容量W
输出:
0 1 1 // 第几个物品选中则为1,不选中则为0
50 // 最大价值
(2)回溯法与分支限界法比较(先区别这两个)

活结点的所有可行子结点被

每个结点只有一次成为活结点的机会

找出满足条件一个解或者特定意义嘚最优解

    首先声明:此两种都有解空间树回溯法n后问题解空间树的解空间树是虚拟的,并不需要在算法运行时构造一棵真正的树结构嘫后再在该解空间树中搜索回溯法n后问题解空间树的解,而是只存储从根结点到当前结点的路径

第一层为空,第二层为a的选择(左子树)与不选择(右子树)第三层为b的选择(左子树)与不选择(右子树),第四层为c的选择(左子树)与不选择(右子树);求解过程分為3步分别对a、b、c元素做决策,该解空间的每个叶子结点都构成一个解(很多情况并非如此);在一个解空间中搜索解的过程构成搜索空間上图中所有叶子结点都是解,所以该回溯法n后问题解空间树的解空间和搜索空间相同

    再比如:下图是四皇后回溯法n后问题解空间树嘚搜索空间,图中每个状态由当前放置的皇后的行列号构成它给出了四皇后回溯法n后问题解空间树的全部搜索过程,只有18个结点其中標有X号的结点无法继续扩展。

    (1*,**)表示第一行第一列放个皇后,其他待搜索;(2,4,1,3)表示第一行第二列、第二行第四列、第三行第一列和第四行第三列各放一个皇后构成一个解;(3,1,4,2)表示第一行第三列、第二行第一列、第三行第四列和第四行第二列各放一个皇后,构荿另一个解;

最后说明活节点:指自身已生成但其孩子结点没有全部生成的结点;扩展节点:是指正在产生孩子结点的结点。

死节点:指由根结点到该结点构成的部分解不满足约束条件或者其子结点已经搜索完毕

    就是回退,如四皇后回溯法n后问题解空间树最左侧,放唍第一行第一列和第二行第三列后无法再向下扩展,则向父节点回退父节点如果还有向下扩展的其他节点则扩展,不能扩展则再向上囙退;代码的世界也有哲学前进的道路走不通时,是深邃骇人的断崖是飞流急湍的河流?于我何干我潇洒回退你耐我何?

    回溯法搜索解空间时通常采用两种策略避免无效搜索,提高回溯的搜索效率:

用约束函数在扩展结点处剪除不满足约束的子树;用限界函数剪詓得不到回溯法n后问题解空间树解或最优解的子树

针对所给回溯法n后问题解空间树,确定回溯法n后问题解空间树的解空间树回溯法n後问题解空间树的解空间树应至少包含回溯法n后问题解空间树的一个(最优)解。

确定结点的扩展搜索规则

以深度优先方式搜索解空間树,并在搜索过程中可以采用剪枝函数来避免无效搜索

//x[i]满足约束条件或界限函数 else i--; //回溯:不存在子结点返回上一层

<1>回溯法n后问题解空间樹表示及求解结果表示----全局变量

* 由于数组下表从1开始,则初始时i=1;tw与tv都为0; * rw为输入数据的总容量;op初始为全0暂存解空间,然后赋值到bestx数組 * 此函数的内部首先是到达叶子节点,也即递归的跳出条件如果价值更优则更新bestx; * 然后是左剪枝和右剪枝操作了:扩展左孩子,需判萣已扩展节点的容量+此节点的容量<=背包容量 不满足则剪枝,然后回溯;扩展右孩子需判定已扩展节点的容量+剩余节点的总容量>背包容量, 不然的话就没有扩展的必要直接剪枝。不管扩展左右孩子都得递归调用dfs_back

(4)分支限界-优先队列(STL)

    顾名思义,树有分枝采用广喥优先的策略,依次搜索活结点的所有分枝也就是所有相邻结点;采用一个限界函数计算限界函数值选择一个最有利的子结点作为擴展结点,使搜索朝着解空间树上有最优解的分枝推进以便尽快地找出一个最优解;

关于优先队列: 

    优先队列式分枝限界的主要特点是將活结点表组组成一个优先队列,并选取优先级最高的活结点成为当前扩展结点步骤如下:

计算起始结点(根结点)的优先级并加入優先队列(与特定回溯法n后问题解空间树相关的信息的函数值决定优先级)。

从优先队列中取出优先级最高的结点作为当前扩展结点使搜索朝着解空间树上可能有最优解的分枝推进,以便尽快地找出一个最优解

对当前扩展结点,先从左到右地产生它的所有孩子结点然后用约束条件检查,对所有满足约束条件的孩子结点计算优先级并加入优先队列

重复步骤②和③,直到找到一个解或优先队列为涳为止

    采用分枝限界法求解的3个关键回溯法n后问题解空间树如下:

如何确定合适的限界函数。如何组织待处理结点的活结点表洳何确定解向量的各个分量。

// # 分支限界优先队列法
// 队列中的节点类型
 int i; // 当前节点在搜索空间的层次
 {// 优先队列按此方式排序
 * ->计算根节点上界及進队
 * ->循环遍历队列条件为非空:出一个节点,
 计算左孩子节点剪枝条件,满足的左孩子计算上界及进队;
 计算右孩子节点上界符合上界條件的右孩子进队;
 (根据容量剪去左孩子,根据上界条件剪去右孩子)
// 进队----不是叶子节点就直接进队是叶子节点则判断是否更优解,昰的话则更新最优解
// 计算边界 就是根据剩余容量的大小计算剩下全部物品装入的价值和装入部分物品的价值
// (部分物品按照单位容量内價值高低的顺序装入---这有点贪心的思想了)
// !# 分支限界优先队列法

   运行上面的代码来计算n=100时的斐波那契数列的值,吃饭前一直闪着屏在计算吃过饭围着操场走了两圈后回来,屏幕还在闪着运行太慢,可以拿空间换时间或者拿金钱换时间。拿金钱换时间比如买个太湖之咣之类的玩玩,短时间之内也能运行出来;拿空间换时间就是动态规划,建一个动态规划数组将之前已经计算的数组放进数组中,下佽使用的时候直接从数据里取不用再计算一遍了。

    运行上面的代码来计算n=100时的斐波那契数列的值两三秒搞定,就是这么神奇

    动态规劃是一种解决多阶段决策回溯法n后问题解空间树的优化方法,把多阶段过程转化为一系列单阶段回溯法n后问题解空间树利用各阶段之间嘚关系,逐个求解

    能采用动态规划求解的回溯法n后问题解空间树的一般要具有3个性质:

最优性原理如果回溯法n后问题解空间树的最优解所包含的子回溯法n后问题解空间树的解也是最优的就称该回溯法n后问题解空间树具有最优子结构即满足最优性原理。

无后效性即某阶段状态一旦确定就不受这个状态以后决策的影响。也就是说某状态以后的过程不会影响以前的状态,只与当前状态有关

有重叠孓回溯法n后问题解空间树即子回溯法n后问题解空间树之间是不独立的,一个子回溯法n后问题解空间树在下一阶段决策中可能被多次使用箌(该性质并不是动态规划适用的必要条件,但是如果没有这条性质动态规划算法同其他算法相比就不具备优势)。

① 分析最优解的性质并刻画其结构特征。

 递归的定义最优解

 以自底向上或自顶向下的记忆化方式计算出最优值

 根据计算最优值时得到的信息构造回溯法n后问题解空间树的最优解。

① 定义合适的动态规划数组  ② 找到合适的状态转移方程  从动态规划数组中找合适的解

 否则在不放入和放入物品i之间选最优解

这样dp[n][W]便是0/1背包回溯法n后问题解空间树的最优解。

* 根据状态转移方程来构造动态 * 2>由于动态规划数组为二维数組则两层for循环里判断是否扩展活动节点 * 动态规划数组已经填充完毕,逆着推出最优解 根据状态转移方程中的条件判断每个物品是否选擇

贪心法的基本思路是在对回溯法n后问题解空间树求解时总是做出在当前看来是最好的选择,也就是说贪心法不从整体最优上加以考虑所做出的仅是在某种意义上的局部最优解。每一次贪心选择都将所求回溯法n后问题解空间树简化为规模更小的子回溯法n后问题解空间树並期望通过每次所做的局部最优选择产生出一个全局最优解所谓贪心选择性质是指所求回溯法n后问题解空间树的整体最优解可以通过一系列局部最优的选择即贪心选择来达到。也就是说贪心法仅在当前状态下做出最好选择,即局部最优选择然后再去求解做出这个选擇后产生的相应子回溯法n后问题解空间树的解。 

贪心法求解回溯法n后问题解空间树的算法框架如下:

    求解部分背包回溯法n后问题解空间树:0/1背包回溯法n后问题解空间树的区别是这里的每个物品可以取一部分装入背包。

    既然贪心是选择当前最优(局部最优)则需要对数據按照一定的规则(求解的目的有关方面)排序,先选择最有利的一个然后再扩展其他。

  背包回溯法n后问题解空间树有重量有价值,求得是利益最大化的则按照单位重量重含有的价值从大到小排序,依次选择

* 求单位重量的价值->按照自定义的格式排序->调用 Knap * 排序后则贪惢循环选择,如果剩余的容量还能容纳当前的则放进去,不能的话跳出循环选择部分放入

    此篇文章根据WHU李老师课件整理,图片也是来の课件为什么有我博客水印我也不清楚,代码也整理之课件;几个方法都放在一个文件中了可能有点乱,不过在main函数之前都有声明烸一种都有注释

// # 回溯法 以此开头
 
main函数中也有,如果用动态规划法则把其他的注释掉就行输入输出别注释。




// # 分支限界优先队列法 // 队列中的節点类型 int i; // 当前节点在搜索空间的层次 * ->计算根节点上界及进队 * ->循环遍历队列条件为非空:出一个节点, 计算左孩子节点剪枝条件,满足的左駭子计算上界及进队; 计算右孩子节点上界符合上界条件的右孩子进队; (根据容量剪去不满足要求的左孩子,根据上界条件剪去不满足要求的右孩子) // 进队----不是叶子节点就直接进队是叶子节点则判断是否更优解,是的话则更新最优解 // 计算边界 就是根据剩余容量的大小计算剩下全部物品装入的价值和装入部分物品的价值 // (部分物品按照单位容量内价值高低的顺序装入---这有点贪心的思想了) // !# 分支限界优先队列法 * 由于数组下表从1开始,则初始时i=1;tw与tv都为0; * rw为输入数据的总容量;op初始为全0暂存解空间,然后赋值到bestx数组 * 此函数的内部首先昰到达叶子节点,也即递归的跳出条件如果价值更优则更新bestx; * 然后是左剪枝和右剪枝操作了:扩展左孩子,需判定已扩展节点的容量+此節点的容量<=背包容量 不满足则剪枝,然后回溯;扩展右孩子需判定已扩展节点的容量+剩余节点的总容量>背包容量, 不然的话就没有扩展的必要直接剪枝。不管扩展左右孩子都得递归调用dfs_back // # 贪心法----非0/1背包回溯法n后问题解空间树,而是部分背包回溯法n后问题解空间树 * 求单位重量的价值->按照自定义的格式排序->调用 Knap * 排序后则贪心循环选择如果剩余的容量还能容纳当前的,则放进去不能的话跳出循环,选择蔀分放入 // # 斐波那契数列 * 根据状态转移方程来构造动态 * 2>由于动态规划数组为二维数组则两层for循环里判断是否扩展活动节点 * 动态规划数组已經填充完毕,逆着推出最优解 根据状态转移方程中的条件判断每个物品是否选择 16 45 第一个物品的重量和价值 15 25 第二个物品的重量和价值 15 25 第三個物品的重量和价值 // 下表0不用,填充0 // # 分支界限优先队列法 // !# 分支界限优先队列法 // # 斐波那契数列 {// 分支限界和回溯法输出的是int型 {// 分支限界和回溯法输出的是int型 // 分支限界优先队列法 e.i=0; //根结点置初值其层次计为0 else //余下物品全部可以装入 // 所有物品的总容量 //求解0/1背包回溯法n后问题解空间树 { //初始调用时rw为所有物品重量和 { //尚未找完所有物品 // 求解背包回溯法n后问题解空间树并返回总价值 // 动态规划后的斐波那契数列
}

因此对每个回溯法n后问题解空间樹需要知道:


1.Define helper()通常我们需要在回溯回溯法n后问题解空间树中使用辅助函数,已接收更多参数

    (2)一个起始索引或结束索引指示正在处悝的部分:对于这个回溯法n后问题解空间树,我们使用substring来指示起始索引不使用明显的int型

3.Base case:基本案例。定义何时将步骤结果添加到最终结果中以及何时返回

5.Choose:在这个回溯法n后问题解空间树中,如果s的子串是回文我们将它添加到步骤中,这意味着我们选择这个子串

6.Explore:在這个回溯法n后问题解空间树中,我们想对剩余的子串做同样的事情所以我们递归调用我们的函数。

7.Un-Choose:我们退回删除所选的子字符串,鉯尝试其他选项


}

  在n*n的棋盘上放置彼此不受攻擊的n个皇后按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子n后回溯法n后问题解空间树等价于在n*n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线

  以上两种算法虽可读性良好,但仍局限于解决8皇后回溯法n后问题解空间树对于解决任何N皇后回溯法n后问题解空间树还要修改程序结构,不能把皇后个数n作为参数传递给函数不具有普遍性。而且程序Φ出现了大量的for循环而且for循环的结构很相似,自然让我们想到迭代回溯即下面我们要讨论的递归回溯和非递归回溯。

  用n元组x[1:n]表示n後回溯法n后问题解空间树的解其中x[i]表示皇后 i 放在棋盘的第 i 行的第x[i]列

  不同行:事先规定好 i 号皇后只能放置在 i 行上这样就解决了同荇的回溯法n后问题解空间树。

  不同列:解向量中的x[i]互不相同即x[i]!=x[j],如此便实现了不同列的限制

  不同斜线:最终任意两个皇后的位置(i,x[i])和(j,x[j])不在同一斜线,即斜率的绝对值不能为1也就是|j-i|!=|x[j]-x[i]|。

  用回溯法解决n后回溯法n后问题解空间树时用完全n叉树表示解空间,可行性約束Place减去不满足行、列和斜线约束的子树

  下面具体解n后回溯法n后问题解空间树的回溯法中,递归函数Backtrack(1)实现对整个解空间的回溯搜索Backtrack(i)搜索解空间中的第 i 层子树。类Queen的数据成员记录解空间结点信息以减少传给Backtrack的参数。sum记录当前已找到的可行方案数

    1、 当i>n时,算法搜索至叶节点得到一个新的n皇后互不攻击放置方案,当前已找到的可行方案数sum加1

    2 、当i<=n时,当前扩展结点Z是解空间中的内蔀结点该结点有x[i]=1,2,3....n共n个儿子结点。对当前扩展结点Z的每一个儿子结点由Place检察其可行性,并以深度优先的方式递归地对可行子树搜索或剪去不可行子树。

算法描述1:————子集树(仅仅隐藏掉行约束)——规定好行位置列位置从第一列开始讨论

23 sum++;  //搜索至叶结点,即討论完最后一个皇后的位置得到一个新的不受攻击放置方案,可行方案数加 1 27 x[t] = i;        //确定第 i 个皇后的列位置 28 if(Place(t))      //检查放置位置是否满足约束条件

  数组x 记录了解空间树中从根到当前扩展结点的路径这些信息已包含了回溯法在回溯时所需要的信息。利鼡数组x 所含的信息可将上述回溯法表示成非递归形式,进一步省去O(n)递归栈空间

26 x[k]+=1;  //x[1]赋初值为0,加1后表示首先从第一列开始放 28 x[k] += 1;  //在沒有超出列的限制且不满足Place函数位置约束时,列后移 33 {  //未到达叶子节点(最后一个皇后)且已经为当前的皇后找到合适的位置,k++后处理下┅个皇后的放置位置 34 k++;    //考虑下一个皇后的放置位置 38 k--;  //由于已经超出列的限制范围且当前正考虑的皇后没有找到合适的位置,则湔一个皇后的位置后移重新来过

 算法描述2:————排列树(隐藏掉行和列约束)——>规定好行位置的同时,列位置从j(不同于上一个瑝后的列)开始讨论

36 sum++;//搜索至叶结点即讨论完最后一个皇后的位置,得到一个新的不受攻击放置方案可行方案数加 1 44 swap(t,i,x);//调回原位置(初始排列组合)———— 众神归位
}

我要回帖

更多关于 数字的n方怎么使用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信