• 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);
            }
        }
    }
    一边喊着救命,一边享受沉沦。
  • 相关阅读:
    【VUE3.0体验】关于路由的一些坑
    TensorFlow中的卷积函数
    TensorFlow源码安装
    ubuntu远程桌面
    TensorFlow图像处理API
    C程序员眼里的Python
    深度剖析HashMap的数据存储实现原理(看完必懂篇)
    golang 互斥锁和读写锁
    golang goroutine的调度
    golang channel的使用以及调度原理
  • 原文地址:https://www.cnblogs.com/fast-walking/p/8930343.html
Copyright © 2020-2023  润新知