方法1:朴素枚举
主要思路:从N x N的棋盘的第一格开始枚举,每格两种情况——放&不放皇后,如果放皇后则进行 行、列、正、反两条对角线是否出现冲突判断,如果不放则进入下一格,直到棋盘内出现N个皇后后输出结果。
特点:简单易懂,很容易想到。
缺点:效率感人,时间复杂度为O(2^n^2)
#include<iostream>
using namespace std;
const int N = 15;
int n;
char g[N][N];//棋盘
bool row[N],col[N],dg[N],udg[N];
//分别是行,列,正对角线,反对角线
void dfs(int x,int y,int s)
{
if(y == n)//越界修正,当遍历完每行最后一格时调到下一行第一个空
{
y = 0;
x++;
}
if(x == n)//遍历所有位置完毕
{
if(s == n)// s 就是皇后的个数
{
for(int i = 0;i < n;i++)
{
puts(g[i]);//相当于出每一行的字符串
}
puts("");
}
return;
}
//放皇后
if(!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])//行列对角线都不冲突
{
g[x][y] = 'Q';//基本和 842 思路一样
row[x] = col[y] = dg[x + y] = udg[x - y + n] = true; //对角线之间的关系可以画个棋盘验证一下
dfs(x,y + 1,s + 1);
row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
g[x][y] = '.';
}
//不放皇后
dfs(x,y + 1,s);//直接跨过当前格子
}
int main()
{
cin >> n;
for(int i = 0;i < n;i++)//初始化
for(int j = 0;j < n;j++)
g[i][j] = '.';
dfs(0,0,0);
return 0;
}
方法2:优化枚举过程
主要思路:按照行来进行枚举,即每一行必须放置一个皇后,相较于法1,当当前行放置一个皇后后,当前行之后的格子便不再考虑了,剪枝剪掉了很多没必要枚举出来的情况,进而提高了效率。
特点:相较于方法省掉了行的数组,节省了空间。且节省了很多错误前提下枚举的时间开销。
#include<iostream>
using namespace std;
const int N = 15;
int n;
bool col[N],dg[N],udg[N];
char g[N][N];
void dfs(int x)
{
if(x == n)//皇后放满了
{
for(int i = 0;i < n;i++)
puts(g[i]);
puts("");
return;
}
for(int y = 0;y < n;y++)
{
if(!col[y] && !dg[x + y] && !udg[x - y + n])//没冲突
{
g[x][y] = 'Q';
col[y] = dg[x + y] = udg[x - y + n] = true;
dfs(x + 1);//直接跳下一行
col[y] = dg[x + y] = udg[x - y + n] = false;
g[x][y] = '.';
}
}
}
int main()
{
cin >> n;
for(int i = 0;i < n;i++)
for(int j = 0;j < n;j++)
g[i][j] = '.';
dfs(0);
return 0;
}