回溯法

搜索与回溯是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。回溯是搜索算法中的一种控制策略。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。

算法思想
      回溯(backtracking)是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。
     下一步是组织解空间以便它能被容易地搜索。典型的组织方法是图(迷宫问题)或树(N皇后问题)。
      一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。
回溯方法的步骤如下:
     1) 定义一个解空间,它包含问题的解。
     2) 用适于搜索的方式组织该空间。
     3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。
回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。因此,回溯算法的空间需求为O(从开始节点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。所以如果要存储全部解空间的话,再多的空间也不够用。

l 回溯法从根结点出发,按照 深度优先 策略搜索(遍历)解空间树,搜索满足约束条件的解。
l
l 初始时,根结点成为一个 活结点 ,同时也称为当前的 扩展 结点 。在当前扩展结点处,搜索向纵深方向移至一个新结点。这个新结点成为一个新的活结点,并成为当前的扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前的扩展结点就成为一个 死结点 (即不再是一个活节点)。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。
l 在搜索至树中任一结点时,先判断该结点对应的部分解是否满足 约束条件 ,或者是否超出 限界函数 的界,也就是判断该结点是否 包含 问题的(最优)解,如果肯定不包含,则跳过对以该结点为根的子树的搜索,即所谓 剪枝 (Pruning);否则,进入以该结点为根的子树,继续按照深度优先策略搜索。
l 在搜索过程中,通常采用两种策略避免无效搜索:
(1)用 约束条件 剪去得不到可行解的子树;
(2)用 限界函数 剪去得不到最优解的子树。
  这两类函数统称为 剪枝函数 (Pruning Function)。

递归回溯(这个重要!!!
void backtrack (int t)
//t 表示递归深度,即对解空间树的第 t 层节点进行深度优先搜索;
{
       if (t>n) output(x); //n 表示解空间树的高度;
       else
         for (int i= f(n,t);i<= g(n,t);i++) {
//f(n,t) g(n,t) 分别表示在当前扩展结点处未搜索过的子树的起始
  编号和终止编号;
           x[t]= h(i); //h(i) 表示在当前扩展结点处 x[t] 的第 i 个可选值;
           if ( constraint(t)&& bound(t)) backtrack(t+1);
// constraint(t) bound(t) 分别表示在当前扩展结点处的约束函数
和限界函数;
           }
}

迭代回溯

void IterativeBacktrack ()
{
  int t=1;
  while (t>0) {
    if ( f(n,t)<= g(n,t))
      for (int i= f(n,t);i<= g(n,t);i++) {
        x[t]= h(i);
        if ( constraint(t)&& bound(t)) {
// 函数 solution(t) 判断在当前扩展结点处是否找到问题的一个可行解;
          if ( solution(t)) output(x);
          else t++;}
        }
    else t--;
    }
}



l当所给问题是从n个元素的集合S中找出满足某种性质的子集时,解空间为子集树
l 当所给问题是从n个元素的集合S中找出满足某种性质的排列时,解空间为排列树


遍历子集树需O(2n)计算时间
void backtrack (int t)
{
  if (t>n) output(x);
  else
    for (int i=0;i<=1;i++) {
     x[t]=i;
    if (constraint(t)&&bound(t)) backtrack(t+1);
      }
}



遍历排列树需要O(n!)计算时间
void backtrack (int t)
{
  if (t>n) output(x);
    else
      for (int i=t;i<=n;i++) {
        swap(x[t], x[i]);
        if (constraint(t)&&bound(t))
           backtrack(t+1);
        swap(x[t], x[i]);
      }
}


装载问题算法描述
n  集装箱数; w[]集装箱重量数组; c第一艘轮船载重量;
cw  在遍历结点处的当前载重量   bsetw 当前最优载重量
private static void backtrack (int i)
   {// 搜索第i层结点
      if (i > n)  // 到达叶结点
        { if (cw>bestw) bestw=cw;return;}
      if (cw + w[i] <= c) {// 搜索左子树
         x[i] = 1;
         cw += w[i];
         backtrack(i + 1);
         cw -= w[i];      }
         x[i] = 0;  // 搜索右子树
         backtrack(i + 1);      }
}

n后问题 算法描述
//place函数测试若将皇后k放在x[k]列是否与前面已放置的k-1个皇后都不在同一列,而且都不在同一斜线上。
bool Queen::Place(int k)
{
  for (int j=1;j<k;j++)
    if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) return false;
  return true;
}
//递归函数backtrack(1)实现对整个解空间的回溯搜索。
void Queen::Backtrack(int t)
{
  if (t>n) sum++;//sum记录当前已找到的可行方案数。
  else
    for (int i=1;i<=n;i++) {
      x[t]=i;
      if (Place(t)) Backtrack(t+1);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值