n皇后问题的关键就是在dfs时判断有没有皇后已经出现在该点的水平线、对角线与反对角线上,其中,水平线很好判断,难点就是将对角线和反对角线上的坐标映射,怎样才能确认两个点是否在一条对角线上呢。
我们假设两个点在一个 y = kx + b上的直线上,当k = 1则线上的点都在一条对角线上,当k = -1则线上的点都在反对角线上,我们首先枚举对角线上的点
当x = 1, y = 1 + b。
当x = 2,y = 2 + b;
当x = 3,y = 3 + b;......
此时若我们从三个数据中找出一个公式且公式的结果是相同的就可以当作我们来判断任意两个点是否在同一条对角线上的依据,不难看出y - x恒等于b,那我们就可以用一个bool数组维护x-y的值,若bool[y - x] = true;说明此对角线上已经有值,则不能放在该点,需要特殊注意的是,若b为负数会导致数组越界,此时要加上一个场数n使得结果恒大于0,即y - x + n;而 x - y + n每次值也相同,则我们只需要记忆x - y + n即可
反对角线亦是同理。y = -x + b;
当x = 1, y = b - 1。
当x = 2,y = b - 2;
当x = 3,y = b - 3;......
此时我们发现不管xy等于几,x + y恒等于 b,则我们记录bool[x + y]便可记录反对角线上是否已经存在一个皇后,若xy都大于0,则不会出现数组越界情况,
但为了方便记忆,我们依旧可以写成x + y + n的形式,与对角线的x - y + n形成对照。
参考代码如下
#include <iostream>
using namespace std;
const int N = 20;
int a[N];
bool st[N], dg[N], udg[N];
int n;
void dfs(int u){
if(u == n){
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < n; j ++){
if(j == a[i]) cout << 'Q';
else cout << '.';
}
cout << endl;
}
cout << endl;
return;
}
for(int i = 0; i < n; i ++)
{
if(!st[i] && !dg[u + i + n] && !udg[i - u + n]){
st[i] = dg[u + i + n] = udg[i - u + n] = true;
a[u] = i;
dfs(u + 1);
st[i] = dg[u + i + n] = udg[i - u + n] = false;
}
}
}
int main()
{
cin >> n;
dfs(0);
return 0;
}