一、概述
輸入整數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來訪問。