【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来访问。

 

 

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