Examples
2018.4.24 回溯法

为了描述问题的某一状态,必须用到该状态的上一状态,而描述上一状态,又必须用到上一状态的上一状态……这种用自已来定义自己的方法,称为递归。

从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到” 尽头 “的时候, 再倒回出发点, 从另一个可能出发, 继续搜索. 这种不断” 回溯 “寻找解的方法, 称为回溯。

 

回溯法是以深度优先方式系统搜索问题解的算法,适用于解决组合数较大的问题。

回溯就是让计算机自动的去搜索,碰到符合的情况就结束或者保存起来,在一条路径上走到尽头也不能找出解,就回到原来的岔路口,选择一条以前没有走过的路继续探测,直到找到解或者走完所有路径为止。

回溯就是一种试探,类似于穷举,但回溯有“剪枝”功能。

回溯法一般有两种实现方式,分别是递归回溯和迭代回溯。

回溯一般使用递归来实现,那个这个递归调用该如何来写呢?进行回溯搜索都会有一系列的步骤,每一步都会进行一些查找。而每一步的情况除了输入会不一样之外,其他的情况都是一致的。这就刚好满足了递归调用的需求。通过把递归结束的条件设置到搜索的最后一步,就可以借用递归的特性来回溯了。

回溯常用模板:

1.非递归模板:

   1: int a[n],i;
   2: 初始化数组a[];
   3: i = 1;
   4: while (i>0(有路可走)   and  (未达到目标))  // 还未回溯到头
   5: {
   6:     if(i > n)                                              // 搜索到叶结点
   7:     {   
   8:           搜索到一个解,输出;
   9:     }
  10:     else                                                   // 处理第i个元素
  11:     { 
  12:           a[i]第一个可能的值;
  13:           while(a[i]在不满足约束条件且在搜索空间内)
  14:           {
  15:               a[i]下一个可能的值;
  16:           }
  17:           if(a[i]在搜索空间内)
  18:          {
  19:               标识占用的资源;
  20:               i = i+1;                              // 扩展下一个结点
  21:          }
  22:          else 
  23:          {
  24:               清理所占的状态空间;            // 回溯
  25:               i = i –1; 
  26:          }
  27: }

2.递归模板:

1: int a[n];
   2: try(int i)
   3: {
   4:     if(i>n)
   5:        输出结果;
   6:     else
   7:     {
   8:         for(j = 下界; j <= 上界; j=j+1)  // 枚举i所有可能的路径
   9:         {
  10:             if(fun(j))                 // 满足限界函数和约束条件
  11:              {
  12:                   a[i] = j;
  13:                   ...                         // 其他操作
  14:                   try(i+1);
  15:                   回溯前的清理工作(如a[i]置空值等);
  16:              }
  17:         }
  18:      }
  19: }

3.迭代回溯伪代码:

void IterativeBacktrack(void){
     int t = 1;
     while(t > 0){
         if(f(n,t) < g(n,t)){
             for(int i = f(n,t); i <= g(n,t); ++i){//这个for 是遍历各个值的意思,实际中写成for循环会有逻辑错误
                 x[t] = h(i);
                 if(constraint(t) && bound(t)){
                     if(solution(t)) Output(x);//solution 判断是否已经得到问题的解
                     else t++;
                 }
                 else t--;
             }
         }
     }
 }

4.递归回溯伪代码:

void Backtrack(int t){
    if(t > n) 
        Output(x);//Output 记录或者输出可行解
    else{
        //f(n,t)和g(n,t)表示在当前结点未搜索过的子树的起始编号和终止编号
        for( int i = f(n,t); i <= g(n,t); ++i){
            x[t] = h(i);
            //constraint和bound分别是约束函数和界限函数
            if(constraint(t) && Bound(t)) 
                Backtrack(t+1);
        }
    }
}

 

 

posted on 2018-04-24 14:41  先行一步  阅读(368)  评论(0编辑  收藏  举报
Examples