1. 程式人生 > >經典回溯問題——八皇後問題

經典回溯問題——八皇後問題

條件判斷 out 進行 過程 sum 增加 不能 return 什麽

算法提出:

在國際象棋棋盤上(8*8)放置八個皇後,使得任意兩個皇後之間不能在同一行,同一列,也不能位於同於對角線上。問共有多少種不同的方法,並且指出各種不同的放法。

算法思路:

  首先我們分析一下問題的解,我們每取出一個皇後,放入一行,共有八種不同的放法,然後再放第二個皇後,同樣如果不考慮規則,還是有八種放法。於是我們可以用一個八叉樹來描述這個過程。從根節點開始,樹每增加一層,便是多放一個皇後,直到第8層(根節點為0層),最後得到一個完全八叉樹。  

  緊接著我們開始用深度優先遍歷這個八叉樹,在遍歷的過程中,進行相應的條件的判斷。以便去掉不合規則的子樹。

  那麽具體用什麽條件來進行子樹的裁剪呢?

  我們先對問題解的結構做一個約定。

  用X[i]來表示,在第i行,皇後放在了X[i]這個位置。

  於是我們考慮第一個條件,不能再同一行,同一列於是我們得到x[i]不能相同。剩下一個條件是不能位於對角線上,這個條件不是很明顯,我們經過分析得到,設兩個不同的皇後分別在j,k行上,x[j],x[k]分別表示在j,k行的那一列上。那麽不在同一對角線的條件可以寫為abs((j-k))!=abs(x[j]-x[k]),其中abs為求絕對值的函數。

  於是下面我們便可以利用一個遞歸的調用來遍歷八叉樹。

  我們首先定義一個訪問某節點所有子節點的函數

void backtrack(int t)
{
    if(t>num) //num為皇後的數目
    {
        sum++;//sum為所有的可行的解
        for(int m = 1;m<num;m++)
        {
            cout<<x[m];//這一行用輸出當遞歸到葉節點的時候,一個可行解
        }
        cout<<endl;
    }
    else
        for(int i = 1;i<=num;i++)
        {
            x[t] = i;
            if(place(t)) backtrack(t+1);//此處的place函數用來進行我們上面所說的條件的判斷,如果成立,進入下一級遞歸
        }
}

下面我們定義了條件判斷函數

bool place(int k)
{
    for(int j = 1;j<k;j++)
        if(abs(x[k] - x[j]) == abs(k-j)||x[j] == x[k])
            return false;
        return true;

}

上面的函數便是按照我們上面說介紹的條件進行判斷。

最後就是主程序的調用了

static int num;
static int *x;
static int sum;
void main()
{
    num = 8;
    sum = 0;
    x = new int[num+1];
    for(int i= 0;i<=num;i++)
        x[i] = 0;
    backtrack(1);
    cout<<"方案共有"<<sum;

    
}

經典回溯問題——八皇後問題