1. 程式人生 > >[回溯演算法] 五大常用演算法之回溯法

[回溯演算法] 五大常用演算法之回溯法

演算法入門6:回溯法

一. 回溯法 – 深度優先搜素                       

1. 簡單概述

       回溯法思路的簡單描述是:把問題的解空間轉化成了圖或者樹的結構表示,然後使用深度優先搜尋策略進行遍歷,遍歷的過程中記錄和尋找所有可行解或者最優解。

基本思想類同於:

  • 圖的深度優先搜尋
  • 二叉樹的後序遍歷

      【

         分支限界法:廣度優先搜尋

         思想類同於:圖的廣度優先遍歷

                                二叉樹的層序遍歷

      】

2. 詳細描述

        詳細的描述則為:

        回溯法按深度優先策略搜尋問題的解空間樹。首先從根節點出發搜尋解空間樹,當演算法搜尋至解空間樹的某一節點時,先利用剪枝函式

判斷該節點是否可行(即能得到問題的解)。如果不可行,則跳過對該節點為根的子樹的搜尋,逐層向其祖先節點回溯;否則,進入該子樹,繼續按深度優先策略搜尋。

        回溯法的基本行為是搜尋,搜尋過程使用剪枝函式來為了避免無效的搜尋。剪枝函式包括兩類:1. 使用約束函式,剪去不滿足約束條件的路徑;2.使用限界函式,剪去不能得到最優解的路徑。

        問題的關鍵在於如何定義問題的解空間,轉化成樹(即解空間樹)。解空間樹分為兩種:子集樹和排列樹。兩種在演算法結構和思路上大體相同。

3. 回溯法應用

       當問題是要求滿足某種性質(約束條件)的所有解或最優解時,往往使用回溯法。

       它有“通用解題法”之美譽。

二. 回溯法實現 - 遞迴和遞推(迭代)                               

        回溯法的實現方法有兩種:遞迴和遞推(也稱迭代)。一般來說,一個問題兩種方法都可以實現,只是在演算法效率和設計複雜度上有區別。
      【類比於圖深度遍歷的遞迴實現和非遞迴(遞推)實現】

1. 遞迴

        思路簡單,設計容易,但效率低,其設計正規化如下:
  1. //針對N叉樹的遞歸回溯方法
  2. void backtrack (int t)  
  3. {  
  4.     if (t>n) output(x); //葉子節點,輸出結果,x是可行解
  5.     else
  6.        for i = 1 to k//當前節點的所有子節點
  7.         {  
  8.             x[t]=value(i); //每個子節點的值賦值給x
  9.             //滿足約束條件和限界條件
  10.           if (constraint(t)&&bound(t))   
  11.                 backtrack(t+1);  //遞迴下一層
  12.         }  
  13. }

2. 遞推

      演算法設計相對複雜,但效率高。
  1. //針對N叉樹的迭代回溯方法
  2. void iterativeBacktrack ()  
  3. {  
  4.     int t=1;  
  5.     while (t>0) {  
  6.         if(ExistSubNode(t)) //當前節點的存在子節點
  7.         {  
  8.             for i = 1 to k  //遍歷當前節點的所有子節點
  9.             {  
  10.                 x[t]=value(i);//每個子節點的值賦值給x
  11.                 if (constraint(t)&&bound(t))//滿足約束條件和限界條件 
  12.                 {  
  13.                     //solution表示在節點t處得到了一個解
  14.                     if (solution(t)) output(x);//得到問題的一個可行解,輸出
  15.                     else t++;//沒有得到解,繼續向下搜尋
  16.                 }  
  17.             }  
  18.         }  
  19.         else//不存在子節點,返回上一層
  20.         {  
  21.             t--;  
  22.         }  
  23.     }  
  24. }  

三. 子集樹和排列樹                                 

1. 子集樹

       所給的問題是從n個元素的集合S中找出滿足某種性質的子集時,相應的解空間成為子集樹。
如0-1揹包問題,從所給重量、價值不同的物品中挑選幾個物品放入揹包,使得在滿足揹包不超重的情況下,揹包內物品價值最大。它的解空間就是一個典型的子集樹。

       回溯法搜尋子集樹的演算法正規化如下:

  1. void backtrack (int t)  
  2. {  
  3.   if (t>n) output(x);  
  4.     else
  5.       for (int i=0;i<=1;i++) {  
  6.         x[t]=i;  
  7.         if (constraint(t)&&bound(t)) backtrack(t+1);  
  8.       }  
  9. }

2. 排列樹

      所給的問題是確定n個元素滿足某種性質的排列時,相應的解空間就是排列樹。
如旅行售貨員問題,一個售貨員把幾個城市旅行一遍,要求走的路程最小。它的解就是幾個城市的排列,解空間就是排列樹。
      回溯法搜尋排列樹的演算法正規化如下:
  1. void backtrack (int t)  
  2. {  
  3.   if (t>n) output(x);  
  4.     else
  5.       for (int i=t;i<=n;i++) {  
  6.         swap(x[t], x[i]);  
  7.         if (constraint(t)&&bound(t)) backtrack(t+1);  
  8.         swap(x[t], x[i]);  
  9.       }  

四. 經典問題                                    

(1)裝載問題
(2)0-1揹包問題
(3)旅行售貨員問題
(4)八皇后問題
(5)迷宮問題
(6)圖的m著色問題

1. 0-1揹包問題

        問題:給定n種物品和一揹包。物品i的重量是wi,其價值為pi,揹包的容量為C。問應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?
        分析:問題是n個物品中選擇部分物品,可知,問題的解空間是子集樹。比如物品數目n=3時,其解空間樹如下圖,邊為1代表選擇該物品,邊為0代表不選擇該物品。使用x[i]表示物品i是否放入揹包,x[i]=0表示不放,x[i]=1表示放入。回溯搜尋過程,如果來到了葉子節點,表示一條搜尋路徑結束,如果該路徑上存在更優的解,則儲存下來。如果不是葉子節點,是中點的節點(如B),就遍歷其子節點(D和E),如果子節點滿足剪枝條件,就繼續回溯搜尋子節點。

程式碼:

  1. #include <stdio.h>
  2. #define N 3         //物品的數量
  3. #define C 16        //揹包的容量
  4. int w[N]={10,8,5};  //每個物品的重量
  5. int v[N]={5,4,1};   //每個物品的價值
  6. int x[N]={0,0,0};   //x[i]=1代表物品i放入揹包,0代表不放入
  7. int CurWeight = 0;  //當前放入揹包的物品總重量
  8. int CurValue = 0;   //當前放入揹包的物品總價值
  9. int BestValue = 0;  //最優值;當前的最大價值,初始化為0
  10. int BestX[N];       //最優解;BestX[i]=1代表物品i放入揹包,0代表不放入
  11. //t = 0 to N-1
  12. void backtrack(int t)  
  13. {  
  14.     //葉子節點,輸出結果
  15.     if(t>N-1)   
  16.     {  
  17.         //如果找到了一個更優的解
  18.         if(CurValue>BestValue)  
  19.         {  
  20.             //儲存更優的值和解
  21.             BestValue = CurValue;  
  22.             for(int i=0;i<N;++i) BestX[i] = x[i];  
  23.         }  
  24.     }  
  25.     else
  26.     {  
  27.         //遍歷當前節點的子節點:0 不放入揹包,1放入揹包
  28.         for(int i=0;i<=1;++i)  
  29.         {  
  30.             x[t]=i;  
  31.             if(i==0) //不放入揹包
  32.             {  
  33.                 backtrack(t+1);  
  34.             }  
  35.             else//放入揹包
  36.             {  
  37.                  //約束條件:放的下
  38.                 if((CurWeight+w[t])<=C)  
  39.                 {  
  40.                     CurWeight += w[t];  
  41.                     CurValue += v[t];  
  42.                     backtrack(t+1);  
  43. 相關推薦

    [回溯演算法] 五大常用演算法回溯

    演算法入門6:回溯法一. 回溯法 – 深度優先搜素                       1. 簡單概述       回溯法思路的簡單描述是:把問題的解空間轉化成了圖或者樹的結構表示,然後使用深度優先搜尋策略進行遍歷,遍歷的過程中記錄和尋找所有可行解或者最優解。基本思想

    資料結構與演算法- 五大常用演算法總結(分治法,回溯,分治限界,貪心演算法,動態規劃法)

    1.分治法(Recurrence and Divide-Conquer)        對於一個規模為n的問題,若該問題可以容易解決(比如說規模n較小)則直接解決,否則將其分解為k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞迴地解決這些子問

    【轉載】五大常用演算法四:回溯

    本文轉自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html 1、概念       回溯演算法實際上一個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就

    五大常用演算法——分治法,動態規劃,回溯,分支界限,貪心演算法

    分治演算法 一、基本概念    在電腦科學中,分治法是一種很重要的演算法。字面上的解釋是“分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。這個技巧是很多高效

    五大常用演算法四:回溯演算法

    1、概念 回溯演算法實際上一個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。 回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重

    五大常用演算法分支定界

    看了五大常用演算法之一這篇博文,感覺理解了很多,可是純粹都是理論,缺少一些示例,所以準備綜合一篇博文,以幫助自己記憶,原文: http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741378.html 一、基本描述

    五大常用演算法一(回溯,隨機化,動態規劃)

    蒙特卡洛演算法 首先要講一下,隨機化演算法之間並不是涇渭分明的,像之前隨機投點法求π也算蒙特卡洛演算法,只有蒙特卡洛演算法與拉斯維加斯演算法有著比較明顯的區別,前者是以高概率給出正確解,但無法確定那個是不是正確解.後者是給出的解一定是正確的,但可能給不出...夠明顯的區別了吧... 基本思想:當所要求解

    五大常用演算法五:分支限界

    分支限界法 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出T中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條件的解

    五大常用演算法五:分支限界(轉)

    轉載自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741378.html 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標

    五大常用演算法 ----回溯演算法

    1、概念       回溯演算法實際上一個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。    回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,

    五大常用演算法思想五:分支限界

    分支限界法 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出T中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條件的解

    五大常用演算法五--分支限界

    一、基本描述 類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。 但在一般情況下,分支限界法與回溯法的求解目標不同。 回溯法的求解目標是找出T中滿足約束條件的所有解 分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束

    演算法設計五大常用演算法設計方法總結

    演算法設計之五大常用演算法設計方法總結 一、【分治法】  在電腦科學中,分治法是一種很重要的演算法。字面上的解釋是“分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。這個

    五大常用演算法:分支限界

    一、基本描述 類似於回溯法,也是一種在問題的解空間樹 T 上搜索問題解的演算法,但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是則是找出滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條件的解中找出使

    五大常用演算法二: 動態規劃演算法1

    非常有必要看一看:一、基本概念    動態規劃過程是:每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。二、

    五大常用演算法(五)分支限界

    分支限界法 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出T中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解

    五大常用演算法——分支限界

    分支限界法 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出T中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條

    五大常用演算法分治法

    五大常用演算法之 分治法 分治法的設計思想是,將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。 分治策略是:對於一個規模為n的問題,若該問題可以容易地解決(比如說規模n較小)則直接解決,否則將其分解為k個規模較小的子問題,這些子問題互相獨立

    五大常用演算法——分支界限

    分支限界法 一、基本描述     類似於回溯法,也是一種在問題的解空間樹T上搜索問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出T中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的一個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解

    程式設計思想 - 五大常用演算法詳解

    https://www.cnblogs.com/brucemengbm/p/6875340.html https://blog.csdn.net/changyuanchn/article/details/51476281 https://www.cnblogs.com/chuninggao/p/