遞迴回溯總結__演算法

來源:互聯網
上載者:User
  8皇后問題是一道非常經典的題目。題目是說,一個N*N的國際象棋棋盤上主放置N個皇后,使其不能相互攻擊,即任何兩個皇后都不能處在棋盤的同一行,同一列,同一條斜線上,試問共有多少種擺法。

        其實,題目就是要找出所有的可能情況,然後排除其中不合格情況,剩下的情況即為所要求的。怎麼找出所有的情況呢。對於8皇后,我們可以使用窮舉法,窮舉出每一种放置方法,然後判斷是否符合題意。如果每次放一行,那就需要8重迴圈才可以解出來。雖然空間複雜度可以小到為0,但是時間複雜度太高。

        書中一般使用回溯法來解此題。仔細分析此題,可以發現:每一行上只能放置一個皇后,然後後面每行放置的皇后,不能與前面的行上放置的皇后在同一列上或者同一對角線上。所以用一個一維數組就可以存放在棋盤上放置的皇后的行列資訊:一維數組的第i個位置存放的數值j就表示,在棋盤的第i行、第j列上放著一個皇后。棋盤的一行就用一個元素來表示,所以不能在同一行就不用判斷了。知道了皇后在棋盤的行列位置後,判斷是否符合後面的兩個條件也比較容易了(對角線只要仔細分析下,兩個二維座標點如果在對角線上,他們的行列座標將會滿足何種情況即可)。

         搞定了資料結構,接著就要考慮如何進行回溯搜尋了。回溯一般借用遞迴來實現。用我的一個ACM非常牛的一個同學的話來說:回溯就是讓電腦自動的去搜尋,碰到符合的情況就結束或者儲存起來,在一條路徑上走到盡頭也不能找出解,就回到原來的岔路口,選擇一條以前沒有走過的路繼續探測,直到找到解或者走完所有路徑為止。就這一點,回溯和所謂的DFS(深度優先搜尋)是一樣的。那現在的關鍵是,怎麼實現搜尋呢。回溯既然一般使用遞迴來實現,那個這個遞迴調用該如何來寫呢。我現在的理解就是,進行回溯搜尋都會有一系列的步驟,每一步都會進行一些尋找。而每一步的情況除了輸入會不一樣之外,其他的情況都是一致的。這就剛好滿足了遞迴調用的需求。通過把遞迴結束的條件設定到搜尋的最後一步,就可以借用遞迴的特性來回溯了。因為合法的遞迴調用總是要回到它的上一層調用的,那麼在回溯搜尋中,回到上一層調用就是回到了前一個步驟。當在當前步驟找不到一種符合條件情況時,那麼後續情況也就不用考慮了,所以就讓遞迴調用返回上一層(也就是回到前一個步驟)找一個以前沒有嘗試過的情況繼續進行。當然有時候為了能夠正常的進行繼續搜尋,需要恢複以前的調用環境。

下面貼出8皇后問題的代碼: #include<iostream>
#include <cmath>
usingnamespace std;

void PrintResult(int*arr,int n)
{
    for (int i=1; i!= n+ 1;++ i)
         cout <<"("<< i << ","<< arr[i]<< ")"<<"" ;
     cout << endl;
}

bool Verify(int*arr,int i)
{
    /* 和前面的i - 1行比較,看當前置放位置是否合法。*/
     for (int k = 1; k != i;++ k)
        if (arr[k]== arr[i]|| abs(i - k)== abs(arr[i]- arr[k]))
            returnfalse ;
    return true ;
}
/* 雖然只用了一個一維數組,但是其中已經儲存了足夠的資訊。
因為每一行只能放一個皇后,所以一維數組的第i個位置存放的
是在第i行的哪一列(第j列)上放置了皇后。這個遞迴函式
每次處理一行,直到第n行(下標從1開始)。*/
void NQueens(int*arr,int i, int n)
{
    /* 嘗試著在第i行的第j列放置一個皇后。*/
     for (int j = 1; j != n+1;++ j)
     {
         arr[i] = j;
        if (Verify(arr, i))
         {
            /* 這個遞迴程式的結束條件是第n行放置完畢,
             所以,當遞迴函式從調用NQueens(arr, i + 1, n)返回時,
             就是回到了第i行,繼續搜尋合適的位置。當第i + 1行的
             所有位置都不能滿足的時候,上面的調用就會返回,也就
             是進行了所謂的回溯。這個回溯不需要顯示的恢複以前的
             調用環境,因為所需要的資訊沒有被破壞。*/
             if (i== n)
                 PrintResult(arr, n);
            else
                 NQueens(arr, i +1 , n);//下一行放置皇后
         }
     }
}

int main()
{
    int n;
     cin >> n;
    int *arr =newint[n +1 ];
     NQueens(arr, 1 , n);

    return 0 ;
}

最後還有一個回溯的模般

回溯法對解空間作深度優先搜尋,因此,在一般情況下用遞迴方法實現回溯法。

void backtrack (int t)
{
if (t>n) output(x);
else
for (int i=f(n,t);i<=g(n,t);i++) {
x[t]=h(i);
if (constraint(t)&&bound(t)) backtrack(t+1);
}
}
供大家學習。

 

註:回溯法是一個既帶有系統性又帶有跳躍性的的搜尋演算法。它在包含問題的所有解的解空間樹中,按照深度優先的策略,從根結點出發搜尋解空間樹。演算法搜尋至解空間樹的任一結點時,總是先判斷該結點是否肯定不包含問題的解。如果肯定不包含,則跳過對以該結點為根的子樹的系統搜尋,逐層向其祖先結點回溯。否則,進入該子樹,繼續按深度優先的策略進行搜尋。回溯法在用來求問題的所有解時,要回溯到根,且根結點的所有子樹都已被搜尋遍才結束。而回溯法在用來求問題的任一解時,只要搜尋到問題的一個解就可以結束。這種以深度優先的方式系統地搜尋問題的解的演算法稱為回溯法,它適用於解一些組合數較大的問題.

詳細介紹參照:百度百科 http://baike.baidu.com/view/699271.htm

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.