backtracking(回溯)

2019-10-19  本文已影响0人  镜中无我

回溯法,是通过递归的方式穷举组合问题的所有可能解,关于这个问题的解决方法大致有如下步骤

1. permutation问题

排列问题是一类典型的可以用backtracking解决的问题,给定n个数字(或者字),打印出所有可能的排列。数学上我们知道这个数量是一个阶乘,很好理解就是n次无放回的选择,这个操作结构如何在用backtracking实现呢?
按照回溯的思路:

solution[n!] //建立一个全局变量,当然也可以不这样做,只是需要在函数递归调用时不断传递解容器和当前解
void backtrack(current_solution, index){
if(current_solution is well_generated)
       print it or record it
       return
else if current_solution is bad_structure
       return // backtrack的迭代函数一般都是无返回值的
for index to space_size
       add space[index] to current_solution
       backtrack(current_solution, index+1)

如果给定一个初始的排列(结构中可能含有重复元素),此时可能会存在重复的解,原因在于横向空间中的重复会导致最终生成完全一致的解,并且递归的重复,所以必须在每一层次上改善解空间(不能对纵向产生影响),即记录已经迭代过的元素,避免重复!但是我们知道在解结构中是可能存在重复元素的,这时我们需要纵向的控制解结构,即通过记录已经构造的解中重复元素的数量来避免破坏解结构。下面的代码是个很好的例子:


    int  array [ 128 ];  //个别存入128个ASCII字元的出现次数
    char  solution [ MAX ];
     
    void  permutation ( int  k ,  int  n )
    {
        if  ( k  ==  n )
        {
            for  ( int  i = 0 ;  i < n ;  i ++)
                cout  <<  solution [ i ];
            cout  <<  endl ;
            return ;
        }
        
        for  ( int  i = 0 ;  i < 128 ;  i ++)    //列举每一个字母
            if  ( array [ i ] >  0 )        //还有字母剩下来,就要列举
            {
                array [ i ]--;          //用掉了一个字母
                
                solution [ k ] =  i ;     // char变数可以直接存入ascii数值
                permutation ( k + 1 ,  n );
     
                array [ i ]++;          //回收了一个字母
            }
    }
  1. next permutation
    相邻元素比较大小,直到前一元素小于后一元素,用遍历过的最小元素代替当前停止元素,然后将其他元素逆序输出。
  2. 有效括号最大长度
    下标存储法,通过记录下标来标记相邻的最大有效字串
  3. 字符串匹配问题一开始应该考虑用组合动态规划去解,考虑能否对0行和0列做初始化,如果能够初始化,则问题多半就能解决,一种求字符串之间距离的算法值得借鉴
上一篇 下一篇

猜你喜欢

热点阅读