1. 程式人生 > >33 N皇後問題

33 N皇後問題

white 規律 for depth OS ext lan pub 沒有

原題網址:https://www.lintcode.com/zh-cn/old/problem/n-queens/#

n皇後問題是將n個皇後放置在n*n的棋盤上,皇後彼此之間不能相互攻擊。

給定一個整數n,返回所有不同的n皇後問題的解決方案。

每個解決方案包含一個明確的n皇後放置布局,其中“Q”和“.”分別表示一個女王和一個空位置。

您在真實的面試中是否遇到過這個題? Yes 樣例

對於4皇後問題存在兩種解決的方案:

[

[".Q..", // Solution 1

"...Q",

"Q...",

"..Q."],

["..Q.", // Solution 2

"Q...",

"...Q",

".Q.."]

]

挑戰

你能否不使用遞歸完成?

標簽 遞歸 Depth-first Search 這題對我而言還是很有難度的,想了半天沒想到解決方法,在網上看了大神們的答案碼了出來,下面梳理一下我自己的理解。 一.遞歸方法 參考:https://www.cnblogs.com/libaoquan/p/7073252.html N皇後問題的兩個最高效的算法 老實說這篇文裏的算法偽碼描述看的我很懵比,至今還未搞懂…… 思路:遞歸方式--回溯算法 這個回溯算法相當於試探法,沿著一條路徑走下去,如果走不通返回上一個節點繼續走,直到窮舉所有路徑。 對於N皇後的放置,要求皇後之間不能彼此攻擊,即任意兩個皇後不能在同一行、同一列、同一對角線上
。棋盤相當於一個二維數組。 1 判斷傳入位置是否超出行最大值(n-1),是的話說明最後一行已經處理好,即已經找到一個解決方案,將該解決方案(字符串數組)push到結果中;否,轉到2; 2 判斷傳入位置能否放Q(與已經放好Q的位置對比,任意兩個皇後不能在同一行、同一列、同一對角線上),能,轉到3;不能,轉到4; 3 該位置字符值置為Q,繼續判斷下一行; 4 傳入當前行的下一個位置(列索引+1),轉到第2步。 判斷傳入位置能否放Q可以單獨定義一個bool類型的函數,可以對棋盤行、列分別檢索,也可以參考博客中的降維方法:把棋盤存儲為一個N維數組a[N],數組中第i個元素的值代表第i行的皇後位置,這樣便可以把問題的空間規模壓縮為一維O(N),在判斷是否沖突時也很簡單,首先每行只有一個皇後,且在數組中只占據一個元素的位置,行沖突就不存在了,其次是列沖突,判斷一下是否有a[i]與當前要放置皇後的列j相等即可。至於斜線沖突,通過觀察可以發現所有在斜線上沖突的皇後的位置都有規律即它們所在的行列互減的絕對值相等,即| row – i | = | col – a[i] | 。這樣某個位置是否可以放置皇後的問題已經解決。轉自此博客 AC代碼:
class
Solution { public: /* * @param n: The number of queens * @return: All distinct solutions */ vector<vector<string>> solveNQueens(int n) { // write your code here vector<vector<string>> result; if (n<=0) { return result; } int * position=new int[n]; for (int i=0;i<n;i++) { position[i]=-1; } int row=0; placeQueen(result,row,position,n); delete []position; return result; } void placeQueen(vector<vector<string>> &result,int row,int * position,int n) { if (row==n) //最後一行處理完,尋找到一個解決方案,將其字符串數組化push到結果中; { string str(n,.); vector<string> temp(n,str); for (int i=0;i<n;i++) { temp[i][position[i]]=Q; } result.push_back(temp); } else { for (int j=0;j<n;j++)//尋找該行可以放置Q的列; { if (CanPlaceQ(row,j,position,n))//找到,則繼續尋找下一行; { position[row]=j; placeQueen(result,row+1,position,n); } //找不到,j++,尋找該行的下一列; } } } bool CanPlaceQ(int row,int col,int * position,int n) { for (int i=0;i<row;i++) { if (position[i]==col||abs(row-i)==abs(col-position[i]))//判斷是否在同一列或同一對角線; { return false; } } return true; } };

遞歸方法其他參考:https://blog.csdn.net/sinat_26230689/article/details/52206498 這個代碼我看了幾遍還是很懵……智商捉急,先把他的思路copy過來:

【解題思路】

深度遍歷+回溯。

1. 從上到下,從左到右,判斷某個位置是否可以放皇後,可以放,轉2,不可以,轉3;

2. 放置皇後,並判斷是否已經放置N個皇後,如果是,記錄結果並回溯(尋找其他解決方案);否則轉1,遞歸判斷下一行能否放置皇後;

3. 判斷本行下一列是否可以放置皇後。如果本列無法放置皇後,剪枝;否則查看下一列能否放置皇後。

即,可以放置,就往下找;放不了,就往回看,拜托上層變一變,看能不能繼續往下找,直到第一層都試過最後一列的位置,程序結束

由於需要記錄所有可行結果並輸出,在每次得到可行結果時,將當前結果保存,並將Q還原為".",方便回溯。

二、非遞歸方法 思路:1、遍歷棋盤的行,尋找可以放置Q的列,找到就記錄位置(行索引對應的列),判斷下一行(註意列置0,因為下一行應從頭開始尋找); 2、如果在當前行找不到可以放置Q的列,應該回溯到上一行,從上一行可以放Q的列的後一位開始,同時該列置為-1(未存放Q的狀態)。若上一行也找不到位置就繼續回溯到上上一行,直到找到可以放Q的位置。如果回溯到第一行也無法找到放Q的位置,說明已經找到所有的解,終止程序; 3、如果放置Q的行是最後一行,說明找到一個解決方案,將其轉成字符串數組push到結果中,此時應該繼續尋找下一個解決方案,即將當前位置放置Q的狀態設置成未存放,從當前位置下一列開始繼續尋找。 非遞歸方法的一個重要問題時何時回溯及如何回溯的問題。程序首先對N行中的每一行進行探測,尋找該行中可以放置皇後的位置,具體方法是對該行的每一列進行探測,看是否可以放置皇後,如果可以,則在該列放置一個皇後,然後繼續探測下一行的皇後位置。如果已經探測完所有的列都沒有找到可以放置皇後的列,此時就應該回溯,把上一行皇後的位置往後移一列,如果上一行皇後移動後也找不到位置,則繼續回溯直至某一行找到皇後的位置或回溯到第一行,如果第一行皇後也無法找到可以放置皇後的位置,則說明已經找到所有的解程序終止。如果該行已經是最後一行,則探測完該行後,如果找到放置皇後的位置,則說明找到一個結果,打印出來。但是此時並不能再此處結束程序,因為我們要找的是所有N皇後問題所有的解,此時應該清除該行的皇後,從當前放置皇後列數的下一列繼續探測。轉自此博客 AC代碼:
class Solution {
public:
    /*
     * @param n: The number of queens
     * @return: All distinct solutions
     */
    bool CanPlaceQ(int row,int col,int * position,int n)
{
    for (int i=0;i<row;i++)
    {
        if (position[i]==col||abs(row-i)==abs(col-position[i]))//判斷是否在同一列或同一對角線;
        {
            return false;
        }
    }
    return true;
}

void placeQueen(vector<vector<string>> &result,int row,int * position,int n)
{
    int i=0,j=0;
    while(i<n)
    {
        while(j<n)
        {
            if (CanPlaceQ(i,j,position,n))
            {
                position[i]=j;
                j=0;//下一行判斷時列從頭開始;
                break; //直接判斷下一行;
            }
            else
            {
                j++;//當前行當前列無法放Q,判斷當前行下一列;
            }
        }

        if (position[i]==-1)//當前行沒有可以放Q的位置,回溯;
        {
            if (i==0)//回溯到第一行也無解,說明找到所有解,退出程序;
            {
                break; 
            }
            --i;//否則回溯到上一行;
            j=position[i]+1;//從上一行可以放Q的下一位開始判斷;
            position[i]=-1;//註意清空上一行位置!!;
            continue;
        }

        if (i==n-1) //最後一行判斷完且找到放Q位置,將當前解決方案放入結果中;
        {
            string str(n,.);
            vector<string> temp(n,str);    
            for (int k=0;k<n;k++)
            {
                temp[k][position[k]]=Q;
            }
            result.push_back(temp);

            j=position[i]+1;//此時不能結束,要找下一個解決方案,繼續判斷當前行下一個位置是否符合要求;
            position[i]=-1;//註意當前位置的狀態要置-1;
            continue;
        }
        i++;
    }
}


vector<vector<string>> solveNQueens(int n)
{
    vector<vector<string>> result;
    if (n<=0)
    {
        return result;
    }
    int * position=new int[n];
    for (int i=0;i<n;i++)
    {
        position[i]=-1;
    }
    int row=0;
    placeQueen(result,row,position,n);
    delete []position;
    return result;
}
};

33 N皇後問題