受伤的皇后
有一个 n × n n \times n n×n的国际象棋棋盘( n n n行 n n n 列的方格图),请在棋盘中摆放 n n n 个受伤的国际象棋皇后,要求:
- 任何两个皇后不在同一行。
- 任何两个皇后不在同一列。
- 如果两个皇后在同一条 45 度角的斜线上,这两个皇后之间行号的差值至少为 3 。
请问一共有多少种摆放方案。
输入描述
输入的第一行包含一个整数
n
n
n。
其中, 1 ≤ n ≤ 10 1 \leq n \leq 10 1≤n≤10。
输出描述
输出一个整数,表示答案。
输入输出样例
示例 1
输入
4
输出
2
思路
- 二进制 + d f s +dfs +dfs
- 可能乍一看需要建立一个二维数组,但实际上可以不用
- 可以把每一行看成一个二进制数,放皇后的地方为1,其余位为0
- 一共有 n n n 行,从第1行开始,逐步向下搜索,这样可以处理“两个皇后不在同一行”
- “不在同一列”可以用“两行上的二进制数不相等”解决
- 前两个要求已经满足了,那么第三个要求:“如果两个皇后在同一条 45 度角的斜线上,这两个皇后之间行号的差值至少为 3”怎么解决呢?
- 由观察可知,当两个皇后在同一条45 ° \degree °角的斜线上时,这两行对应的二进制数之间有如下关系: 较 大 的 二 进 制 数 / 较 小 的 二 进 制 数 = 2 两 个 行 号 之 差 的 绝 对 值 较大的二进制数/较小的二进制数=2^{两个行号之差的绝对值} 较大的二进制数/较小的二进制数=2两个行号之差的绝对值
- 行号的差值至少为3,则两个行号之差的绝对值 ≥ \geq ≥ 3即可
如何 d f s ? dfs? dfs?
- 设一个全局变量存储可行的方法数
- 将参数设为行号,从0开始
- 当行号为n时,说明已经产生了一种可行的方法,这时方法数++,返回上一行
- 否则,从某一行的第一个格子开始放皇后,如果这个格子可以放皇后,则将皇后放在这一行,然后继续搜索下一行,否则换下一个格子,当这一行的所有格子都不能放皇后时,则返回,并且将上一行的皇后撤去(因为是上一行的皇后影响了这一行)
怎么“放皇后”?
- 设立一个"栈",用于存放皇后
- 这个“栈”不是定义上的栈,而可以像数组一样遍历,但是又有栈顶指针
- 放皇后时入栈,撤回皇后时出栈
如何判断当前格子可以放皇后?
-
设立一个判断函数,返回值为布尔类型,为真表示可以放,为假表示不能放
-
结合上面的特殊栈,将当前位置放上皇后之后这行表示的二进制数k与栈中的所有已有元素对比,若:
- 1, k与栈中的某一个元素相等
- 2,k与某元素的正商恰好为2的(行号之差的绝对值)次幂并且行号之差小于3
则不能放皇后,否则可以放
-
注:由于每一行只能放一个皇后,则每一行的二进制数都是2的幂,则可以用位运算简化
代码如下
#include <iostream>
using namespace std;
int tmp_stack[11]; //特殊栈
int top; //栈顶指针
int ans; //皇后的放法
int n; //棋盘行数
/**
* @brief 判断是否可以放皇后
*
* @param n 要放皇后的那一层表示的二进制数(这里转化为十进制数)
* @return true 能放皇后
* @return false 不能放皇后
*/
bool is_ok(int n) {
for (int i = 0; i < top; i++) {
if (n == tmp_stack[i] || ((n / tmp_stack[i] == (1 << (top - i)) ||
tmp_stack[i] / n == (1 << (top - i))) &&
top - i < 3)) {
return false;
}
}
return true;
}
/**
* @brief dfs搜索放皇后的方法
*
* @param row 行号
*/
void dfs(int row) {
if (row == n) {
ans++;
return;
}
for (int i = (1 << (n - 1)); i; i >>= 1) {
if (is_ok(i)) {
tmp_stack[top++] = i;
dfs(row + 1);
top--;//撤回皇后
}
}
}
int main(void) {
scanf("%d", &n);
dfs(0);
printf("%d\n", ans);
return 0;
}