1. 程式人生 > >經典回溯演算法之n皇后問題

經典回溯演算法之n皇后問題

這是來源於國際象棋的一個問題。n皇后問題要求在一個n×n格的棋盤上放置n個皇后,使得它們彼此不受攻擊。按照國際象棋的規則,一個皇后可以攻擊與之處在同一行或同一列或同一條斜線上的其他任何棋子。因此,n後問題等價於要求在一個n×n格的棋盤上放置n個皇后,使得任何兩個皇后不能被放在同一行或同一列或同一條斜線上。

求解過程從空棋盤開始,設在第1行至第m行都已經正確放置了m個皇后,再在第m+1個行上找合適的位置放第m+1個皇后,直到在第n行也找到合適的位置放置第n個皇后,就找到了一個解。接著改變第n行上皇后的位置,希望獲得下一個解。另外,在任一行上有n種可選的位置。開始時,位置在第1列,以後改變時,順次選擇第2列、第3列、....... 、第n列。當第n列也不是一個合理的位置時,就要回溯,去改變前一行的位置。

回溯法解題步驟

(1)針對所給問題,定義問題的解空間。

(2)確定易於搜尋的解空間結構。

(3)以深度優先的方式搜尋解空間。

回溯法求解4皇后問題的搜尋過程如下圖所示:


很顯然,由上述搜尋過程我們可以看出第i個皇后一定在第i行上,所以我們可以利用一個數組來儲存每一個皇后所在的列。

C#版程式碼如下:

        private void button1_Click(object sender, EventArgs e)
        {
            int n =8;  //皇后的個數
            N_Queen(n);
        }

        public Boolean Place(int[] Column, int index)
        {          
            for (int i = 1; i < index; i++)
            {   //當兩個皇后所在列的差值等於所在行的差值時,表明兩個皇后在同一斜線上
                int Colunm_differ = System.Math.Abs(Column[index] - Column[i]);  //兩個皇后所在列的差
                int Row_differ = System.Math.Abs(index - i);  //兩個皇后所在行的差
                if (Column [i]==Column [index ] || Colunm_differ==Row_differ  ) //有皇后與其在同列或同一斜線上
                {
                    return false ;
                }
            }
            return true ;  //沒有皇后與其同行、同列、同對角線
        }

        public void N_Queen(int n)
        {
            int[] Column_Num = new int[n + 1];
            int index = 1;  //index是當前行,初值為1,從第1個皇后開始放置          
            int answer_num = 0;  //記錄n皇后問題解的個數

            //其實是為了初始化各皇后的位置,可以不寫:
            //for (i = 1; i <= n; i++)
            //{
            //    Column_Num[i] = 0;
            //}

            while (index > 0)
            {
                Column_Num[index]++;
                while (Column_Num[index] <= n && !Place(Column_Num, index)) //尋找皇后的位置
                {
                    Column_Num[index]++;  //移到下一列
                }
                if (Column_Num[index] <= n)
                {
                    if (index == n)  //n個皇后全部放置完畢
                    {
                        answer_num++;

                        //輸出每一種方案的結果:
                        txt1.Text = txt1.Text + "\r\n" + "方案" + answer_num;
                        for (int i = 1; i <= n; i++)
                        {
                            txt1.Text = txt1.Text +"  "+ "(" + i + "," + Column_Num[i] + ")"; //(皇后i,位置)
                        }

                        //繼續尋找另一種方案:以下三種均可以
                        index--;

                        //或者:
                        //Column_Num[index] += n;

                        //又或者:
                        //for (int i = 1; i < n; i++)
                        //{
                        //    Column_Num[index]++;
                        //}
                    }
                    else  //繼續尋找下一個皇后的位置
                    {
                        index++;
                        Column_Num[index] = 0;
                    }
                }
                else
                {
                    index--;  //當前皇后無法放置,回溯至上一個皇后
                }
            }           
        }

當n=8時,得到的所有的方案如下圖(不考慮對稱性):


當n持續增大時,我試了試n=15,發現時間複雜度已經超出我的心裡預期,最終沒等它繼續執行下去。

感謝您的閱讀~