【LeetCode】51. N-Queens 輸出N皇后的棋盤佈局

一、概述

輸入整數N,輸出N皇后的所有可能情形。

之前只做過判斷N皇后,沒做過輸出N皇后,感覺有點麻煩。實際上輸出的確挺麻煩,而且我的代碼還有優化空間。

二、分析

整體思路就是用DFS了,所以我只介紹我自己的思路。簡而言之,我是先保存座標,然後座標化爲棋盤這樣輸出的。

使用DFS+Hash得到座標。

思路如下:

整個棋盤有N行N列2*N-1左對角線2*N-1右對角線。每當我們確定一個皇后的位置,我們就能確定1行1列左對角線1右對角線之後不能再有皇后在這裏了。於是,DFS的出口就是確定第N個皇后的位置。DFS的遞歸體就是在所有可能裏面找。

注意以下一點,棋盤是上下左右對稱的,所以,如果我們按列找,那麼DFS可以按列向後遞歸,先找第一列,再找第二列。。。直到第N列。同時由於對稱,第一列最多隻要令行座標爲N/2時就可以停止——另外一半可以通過前一半對稱找到,比如說座標(1,1)可以有皇后,那麼一定座標(N,1)也可以有皇后。這樣在最初我們就減少了一半的情況(雖然實際沒什麼用,而且令代碼複雜了很多)。

在第一列確定後,我們令行哈希,左對角哈希,右對角哈希的值置爲1,去找第二列中滿足三個哈希爲0的情況。找到第二列之後找第三類。。。這就是DFS的遞歸體的過程。

找到所有座標後,通過assign函數和reverse函數的調用可以很容易的形成棋盤:assign可以讓string用同一個字符*n進行初始化,reverse則負責翻轉棋盤。

代碼如下:

class Solution {
    using pii = pair<int, int>;
    vector<vector<pii>> pii_ans;
    int hang[100]={0};
    int xie1[100]={0};
    int xie2[100]={0};
    void DFS(int j,vector<pii>& tmp_ans,int n,int flag)
    {
        if(j==n+1)
        {
            pii_ans.push_back(tmp_ans);
            return;
        }
        else
        {
            int now_n=n;
            if(j==1&&flag==0)
                now_n=n/2+1;
            if(j==1&&flag==1)
                now_n=n/2;
            for(int i=1;i<=now_n;++i)
            {
                if(hang[i]==0&&xie1[i+j-1]==0&&xie2[j-i+8]==0)
                {
                    hang[i]=1;
                    xie1[i+j-1]=1;
                    xie2[j-i+8]=1;
                    tmp_ans.push_back({i,j});
                    DFS(j+1,tmp_ans,n,flag);
                    hang[i]=0;
                    xie1[i+j-1]=0;
                    xie2[j-i+8]=0;
                    tmp_ans.pop_back();
                }
            }
        }
    }
public:
    vector<vector<string>> solveNQueens(int n) {
        int flag=(n%2==0)?1:0;
        vector<pii> tmp_ans;
        vector<vector<string>> res;
        if(n==1)
        {
            vector<string> tmp;
            tmp.push_back("Q");
            res.push_back(tmp);
            return res;
        }
        DFS(1,tmp_ans,n,flag);
        for(int i=0;i<pii_ans.size();i++)
        {
            vector<string> tmp_str;
            for(int t=0;t<n;t++)
            {
                string s;
                s.assign(n,'.');
                tmp_str.push_back(s);
            }
            for(int j=0;j<n;j++)
            {
                auto[x,y]=pii_ans[i][j];
                tmp_str[x-1][y-1]='Q';
            }
            res.push_back(tmp_str);
            if(pii_ans[i][0].first<ceil(1.0*n/2)||(pii_ans[i][0].first==ceil(1.0*n/2)&&flag==1))
            {
                reverse(tmp_str.begin(),tmp_str.end());
                res.push_back(tmp_str);
            }
        }
        return res;
    }
};

注意一點:以N=5爲例,當第一列的皇后在第一行或第二行時,可以無腦翻轉,但是當在第三行時,翻轉可能導致重複,因此對於第三行不要翻轉。這個小聰明沒有在時空複雜度上有所收益,倒是在代碼複雜度上貢獻不少。這讓我很是尷尬。

對於左右對角線和行列的值的關係的確定,可以參見下圖:

藍色是右對角線序號,紅色是左對角線序號,左邊的豎着的爲x,下面的橫着的爲y。可以觀察到在同一列上,藍色與紅色的值的和不變,均爲7+2*y,而藍色的值爲x+y-1,於是紅色的值爲y-x+8。需要自己畫一畫才容易看出來。

三、總結

首先,DFS對於某一層的優化是沒卵子用的,要優化就要每層都優化纔行。

其次,string的assign函數很好用;

最後,pair可以用first和second來訪問。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章