【回顾&小结】回溯法

回溯法の定义

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。

以上来自百度百科。

回溯法の解空间

就是要找出这个问题所有的解,在这些解里面筛选出最优解
举个例子,0-1背包的问题,假设我们有A,B,C三个物品,我们要判断选择装进哪些物品。从我们的背包里没有任何东西开始,我们按顺序一个个物品来判断是不是把它装进去。那么我们就可能出现两种想法:
想法一
对每一个物品判断是装还是不装,然后画出一个不规范的的图:
图片描述

在这个图中,连线旁边的数字:1表示装入背包中,0表示不装入背包。第一层中,我们判断是否放入A,放入就是1那条岔路,反之是0那条岔路,所以很直观地,我们就可以得到所有可能出现的8个情况:

{(1,1,1),(1,1,0),(1,0,1),(1,0,0),(0,1,1),(0,1,0),(0,0,1),(0,0,0)}

想法二
我们假设自己一开始要从三个里面选一个来装入,所以就有了:
图片描述

(但是01背包问题并不适合用这种方法来解,所以这里就是与上面的树做一个对比)
如上图则表示,我们从A,B,C三个中先选择一个装入,如果第一轮选择了A,那么剩下就还有选择B或者C

而想法一的想法,其实就能形成一颗子集树,它在每次选择的时候,是在一个有限选择集合S里面进行选择,01背包中,这个S就是{0,1},而想法二能形成一颗排列树,它在每次进行选择的时候是在给出的选项S中作选择,这里是{A,B,C}。
所以回溯法的写法,也有两种,子集树的写法和排列树的写法,做题的时候要自己选择更加合适的方法来解决。

回溯法の公式

每种算法都可以选择递归和迭代的方式来写,显然这里用迭代方式来写是很麻烦的,可以说也没有人选择这样的方法,所以我们都采用递归来写。

子集树写法
我们的目的就是要遍历子集树上的每个解法,为了判断每一个解对不对,所以我们应该是深度遍历,所以我们需要:t(树的深度),数组x(每一层的结果选择)。先写出中心的遍历函数:

void backtrack(int t){
    if(t>=n){ 
           output();   //此时已到达叶子结点,用于输出一个结果
           return;
    }
    
    //对分叉点遍历0,1两种情况    
    for(int i=0;i<=1;i++){
        x[t]=i;  //给第t层代表的选择赋可能选择的值
        if(ok(t))  
            //ok是判断目前的一个解有可行的机会 
            backtrack(t+1);  
    }
        
}

此时我们应该注意,x数组的初始值应该都为0。

排列树写法
也和子集树一样要遍历子集树上的每个解法,我们同样需要:t(树的深度),数组x(每一层的结果选择)。写出中心的遍历函数:

void backtrack(int t){
    if(t>=n){ 
           output();   //此时已到达叶子结点,用于输出一个结果
           return;
    }
    
    //从第t个单元开始进行交换排列  
    for(int i=t;i<n;i++){
        swap(x[t],x[i]);  //交换两个排列顺序,得到了一个解
        if(ok(t))  
            //ok是判断目前的一个解有可行的机会 
            backtrack(t+1);  
        swap(x[t],x[i]);  //恢复原样,给后面的好继续交换判断
    }
        
}

此时我们应该注意,因为排列树其实就是写出所有可能的排列,所以x数组里面每个单元的内容都应该是不一样的,我们在初始化的时候就应该依次赋予可能的解。
总结比较,在子集树的公式中,for循环中的i是可能的取值,排列树的公式中,for循环中的i是所有可能取值的存放位置

举个栗子

n皇后问题,要求在一个n行n格的棋盘中摆放n个皇后,皇后不能在同一行,同一列或者同一斜线,我们要找出有多少个解法。这个问题,我们在选择x数组上,一个一维数组就足够用,下标表示在第几行,对应的值表示在第几列。
子集树方法
假如n=4,初始化x数组中每个取值为0,那么我们的for循环中i可能有0,1,2,3四种选择,然后就是填写ok()函数,根据约束的条件可知,ok()函数里应该填写判断

皇后不能在同一行,同一列或者同一斜线

图片描述

排列树方法
假如n=4,初始化x数组中每个取值为0,1,2,3,ok函数也和子集树一样,我们的for循环中i也可能有0,1,2,3四种选择,因为是采用了交换,不出现两个重复的值,所以和子集树相比,走过的路要少一些。
图片描述

效率の提高

为了提高算法的效率,我们可以从ok()函数来入手:

  1. 计算简单
  2. 剪枝多(避免走行不通的路)

但是这两个条件是互相矛盾的。

最后...

这是一个挣扎的小渣渣写的文章,所以可能会有错误,各位大佬多指教QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值