算法:回溯+剪枝

回溯算法的定义:

在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。

解空间树:依据待解决问题的特性,用树结构表示问题的解结构、用叶子表示问题的解的一颗树。

最初接触回溯算法,应该是走迷宫问题用到DFS。对于一些直观的图论问题,回溯是很符合常识的思路,“此路不通,原路返回,另寻他路”,这是非常朴素的回溯思路,以致于当时根本没有意识到这也算是一种算法思想。

一个错误的认识是,回溯只能用于图论问题,其背后的根源是缺少将一般的问题抽象出解空间树的能力,以及将一般问题分解为若干状态的能力。

重新认识回溯思想是遇到全排列问题,这个问题里没有明显的图和树,当时甚至没有往DFS上想,事后看了题解,苦笑、默叹,以为妙绝。

回溯与剪枝:回溯的本质是枚举和暴力,这意味着他的效率不会高到哪里去,常常需要剪枝来优化。

回溯算法小归纳:
1.回溯就是从当前结点开始递归,如果递归成功则返回true,递归失败就把当前结点移出解空间 (pop_back()),这样就完成了回溯
2.剪枝:如果当前结点不满足条件,则剪枝(在循环体中表现为break,在函数中体现为return false)

DFS深度遍历函数模板


void dfs()//参数用来表示状态  
{  
    if(到达终点状态)  
    {  
        ...//根据题意添加  
        return;  
    }  
    if(越界或者是不合法状态)  //剪枝
        return;  
    if(特殊状态)//剪枝
        return ;
    for(扩展方式)  
    {  
        if(扩展方式所达到状态合法)  
        {  
            修改操作;//根据题意来添加  
            标记;  
                if(越界或者是不合法状态)  //剪枝
                return;  
                if(特殊状态)//剪枝
                return ;
            dfs();  
            (还原标记);  
            //是否还原标记根据题意  
            //如果加上(还原标记)就是 回溯法  
        }  
 
    }  
} 

例题:

主要思路:利用深度遍历,每次划分数字,从第三次开始判断数字是否合规

//可以利用本题涉及的一些规则进行剪枝并回溯:如  每次划分的数不能超过int的上限;

每次划分的数的首位不能为0,除非本身为0;

拆分出的数大于前两个数的和则此次拆分失败;

剩余拆分的数的位数,小于已经拆分的数的位数,则拆分失败(因为都是非负数,那么,后面的数肯定大于等于前面拆分的数,即后面数的位数肯定大于等于前面数的位数)

上面一行的思路其实不对,因为对于最后一个拆分的数来说,没有后续的数了,而且仔细 想想,其实这条规则已经包含在“拆分出的数大于前两个数的和则此次拆分失败”这条中了。

class Solution {
public:
    bool dfs(vector<int> &re,string &S,int index,int pre,long long sum)//index表示当前的位置
    {
      //每次拆分字符串,从第三次开始进行判断;
      //判断第三次拆分的数是不是前两个数的和,不是就说明此路不通进行剪枝
      //
      if(index>=S.length()) return re.size()>=3;//题目要求至少是3个
      long long cur=0;
      for(int i=index;i<S.length();i++)
      {
            if (i > index && S[index] == '0')  //如果出现首位为0的情况就剪枝
               return false; 
            
              
       //   if(i>index&&S[i]=='0') return false;
         cur=cur*10+S[i]-'0';
         if(cur>INT_MAX) return false;//题目要求要小于int的最大值
         
         if(re.size()>=2)//第三个数开始才需要进行判断
         {
            if(cur<sum) continue;//继续拆
            else if(cur>sum) return false;//此路不通
        }

          re.push_back(cur);
          if( dfs(re,S,i+1,cur,pre+cur)) return true;
          //else//回溯 
          //{ 
            re.pop_back();
          //}
      }


      return false;

    }



    vector<int> splitIntoFibonacci(string S) {
           vector <int> re;
           dfs(re,S,0,0,0);
           return re;
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_42067304/article/details/110921425