一、概述
输入整数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来访问。