ACwing843 - n皇后问题(经典dfs)
n-皇后问题是指将 n 个皇后放在 n∗n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数n,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数n。
输出格式
每个解决方案占n行,每行输出一个长度为n的字符串,用来表示完整的棋盘状态。
其中”.”表示某一个位置的方格状态为空,”Q”表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1 ≤ n ≤ 9
输入样例:
4
输出样例:
.Q…
…Q
Q…
…Q.
…Q.
Q…
…Q
.Q…
题目大意:
要求输出方案数,有n * n 的棋盘,需要放n皇后(用Q表示),空地用’ . ’ 表示,关于皇后放置有以下几个要求:
- 任意两个皇后不能在同一行
- 任意两个皇后不能在同一列
- 任意两个皇后不能再同一对角线
要求输出摆好的棋盘。
解题思路:
经典n皇后问题,用dfs解决,数据范围很小,dfs暴搜 + 剪枝即可AC,这里介绍两种搜索思路:
思路一:从第1行搜索到第n行, 按列判断。
这个思路是按行搜索,标记每一列和主次对角线,按行搜索可以保证任意两个皇后都不在同一行,然后去搜这一行的1 - n 列,开标记数组book标记这一列上是否有皇后,gh和ugh标记主次对角线,关于主次对角线:
- y = - x + b 对于每一个点(y + x)都有唯一的 b 值对应。
- y = x + b 对于每一个点(y - x) 有唯一的b 值对应。
因为第二个涉及到了y - x有可能负数,所以开大一点标记数组,并把式子映射为y - x + n 。因为都有唯一一个 b 所对应,所以可以根据 x ± y 的值是否被标记过判断该对角线上有无皇后,之后按照dfs模板去搜,状态改变状态还原,如果x > n 则证明搜完了且一定存在解(因为继续往下搜索的前提是这一行可以找到放皇后的位置),输出并return;
Code1:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 25;
int n;
char mp[N][N];
bool gh[N], ugh[N], book[N];
void dfs(int s)
{
if (s == n + 1)
{
for (int i = 1; i <= n; i ++) puts(mp[i] + 1);
puts("");
return;
}
for (int i = 1; i <= n; i ++)
{
if (!book[i] && !gh[s + i] && !ugh[n + s - i])
{
book[i] = true;
gh[s + i] = true;
ugh[n + s - i] = true;
mp[s][i] = 'Q';//改状态并继续搜
dfs(s + 1);
book[i] = false;
gh[s + i] = false;
ugh[n + s - i] = false;
mp[s][i] = '.';//还原状态
}
}
return;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
mp[i][j] = '.';
dfs(1);//从第一行开始搜
return 0;
}
思路二:枚举所有格子的状态
顾名思义:每个格子有放和不放两种状态,对于每个格子枚举即可。每次枚举完以后枚举(x, y + 1, s),如果y > n 说明这行枚举完了,y = 1, x++, 继续枚举下一行,如果x > n 则说明整个棋盘枚举完了,判断s 是否等于 n ,这里的 s 是指当前已经放下了几个皇后,如果等于n则输出棋盘,之后return。
每个格子只有放和不放,不放的话很好理解,直接dfs(x, y + 1, s),如果放的话需要判断一下这个格子是不是能放皇后,需要开一个行标数组,列标数组,主次对角线,都判断一下,然后选择是否放皇后即可。
Code2:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 25;
int n;
char mp[N][N];
bool gh[N], ugh[N], col[N], row[N];
void dfs(int x, int y, int s)
{
if (y > n)
{
y = 1;
x++;
}
if (x > n)
{
if (s == n)
{
for (int i = 1; i <= n; i ++) puts(mp[i] + 1);
puts("");
}
return;//return一定要写在 if 外面,因为不管方案是否可行都搜完了要返回,不然会MLE。
}
dfs(x, y + 1, s);
if (!row[x] && !col[y] && !gh[x + y] && !ugh[x - y + n])//判断是否可以放皇后
{
row[x] = col[y] = gh[x + y] = ugh[x - y + n] = true;//行标列标主次对角线都判断一下
mp[x][y] = 'Q';//状态修改/还原
dfs(x, y + 1, s + 1);
row[x] = col[y] = gh[x + y] = ugh[x - y + n] = false;
mp[x][y] = '.';
}
return;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
mp[i][j] = '.';
dfs(1, 1, 0);//从(1, 1)开始搜,当前已经放了0个皇后
return 0;
}