回溯算法


回溯的描述:

回溯算法將解空間看作一定的結構,通常為樹形結構,一個解對應於樹中的一片樹葉。算法從樹根(即初始狀態出發),嘗試所有可能到達的結點。當不能前行時就后退一步或若干步,再從另一個結點開始繼續搜索,直到嘗試完所有的結點。也可以用走迷宮的方式去理解回溯,設想把你放在一個迷宮里,想要走出迷宮,最直接的辦法是什么呢?沒錯,試。先選一條路走起,走不通就往回退嘗試別的路,走不通繼續往回退,直到走遍所有的路,並且在走的過程中你可以記錄所有能走出迷宮的路線。

 

回溯與八皇后問題

在國際象棋中,皇后是最強大的一枚棋子,可以吃掉與其在同一行、列和斜線的敵方棋子。八皇后問題是這樣一個問題:將八個皇后擺在一張8*8的國際象棋棋盤上,使每個皇后都無法吃掉別的皇后,一共有多少種擺法?

如果使用暴力算法的話要在8*8個格子中選擇8個格子來擺放皇后,一共要嘗試C864次,十億級別的嘗試次數!

稍加分析,我們可以得到一個不那么暴力的辦法,顯然,每行每列最多只能有一個皇后,如果基於這個事實進行暴力破解,那結果會好得多。安排皇后時,第一行有8種選法,一旦第一行選定,假設選為(1,i),第二行只能選(2,j),其中j!=i,所以有7種選法。以此類推,需要窮舉的情況有8!=40320種。

我們先降下規模來看看4皇后問題:4個皇后在4*4的格子里各自安排不打架,一共有多少種安排方法?

試着來窮舉一下,真的需要4!=24次嘗試嗎?現在我們把第一個皇后放在第一個格子,被塗黑的地方是不能放皇后的。

第二行的皇后只能放在第三格或第四格,按照回溯我們要先嘗試放在第三格,則:

這樣第三位皇后無論放哪里都難逃被吃掉的厄運。於是在第一個皇后位於1號,第二個皇后位於3號的情況下問題無解。我們只能返回上一步來,給2號皇后換個位置。

顯然,第三個皇后只有一個位置可選。當第三個皇后占據第三行藍色空位時,第四行皇后無路可走,於是發生錯誤,返回上層調用(3號皇后),而3號也別無可去,繼續返回上層調用(2號),2號已然無路可去,繼續返回上層(1號),於是1號皇后改變位置如下,繼續搜索。

話說到這里,我們對“回溯法”已經有了基本概念。然而所謂知易行難,理解算法和將算法寫出來完全是兩回事。下面來看看代碼:

代碼不長,注釋很多

#include <iostream>
using namespace std;

bool isOk(int c[], int row, int n); //函數:判斷能否在第row行第c[row]列插入一個皇后
void queen(int row, int c[], int n, int& total);    //函數:回溯的核心部分

int main()
{
    int n;  //皇后的數量
    cout << "Please enter the number of queen:\n";
    cin >> n;
    int* c = new int[n];    //記錄皇后的位置,例如c[i]的值為j表示第i行的皇后放在第j列
    int total = 0;  //解決方案的種數
    queen(0, c, n, total);
    cout << "The number of solution is:\n" << total;
    return 0;
}

void queen(int row, int c[], int n, int& total)
{
    if (row == n)   //當row等於n的時候表示皇后全部擺放完畢
    {
        for (int i = 0; i < n; i++)
            cout << c[i] << " ";
        cout << endl;
        total++;
    }
    else    //皇后還未擺放完,則執行下面程序
    {
        //遍歷所有的列,看看第row行皇后可以放在第幾列
        for (int col = 0; col < n; col++)   
        {
            c[row] = col;
            //如果可以放在row行col列則繼續擺放下一行
            if (isOk(c, row, n))    
                queen(row+1, c, n, total);
            //如果不可以放在row行col列則試試下一列
        }
        //如果循環了所有的列都不能擺放,則會回溯到前一層函數改變上一行皇后的擺放
    }
}

bool isOk(int c[], int row, int n)
{
    for (int i = 0; i < row; i++)
    {   //第row行皇后不能和任意之前的皇后在同一列或 \方向或 / 方向
        if (c[i] == c[row] || c[row]-row == c[i]-i || c[row]+row == c[i]+i)
            return false;
    }
    return true;
}

 

 

 算法是逐行安排皇后的,其參數row為現在正執行到第幾行(row是從0開始的,0表示第一行)。n是皇后數。

 

 

參考博客:https://www.cnblogs.com/bigmoyan/p/4521683.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM