在 n * n 的棋盘上放置了彼此不受攻击的 n 个皇后。按照国际象棋的规则,皇后可以攻击与之在同一行、同一列、同一斜线上的棋子。请在 n * n 的棋盘上放置 n 个皇后,使其彼此不受攻击。
如果棋盘如下图所示,我们在第 i 行第 j 列放置一个皇后,那么第 i 行其他位置,第 j 列其他位置,同一斜线上的其他位置,都不能再放皇后。
不可能杂乱无章地尝试每个位置,需要有策略地求解,可以以行为主导。
1 在第 1 行第 1 列放置第 1 个皇后。
2 在第 2 行放置第 2 个皇后。第 2 个皇后的位置不能与第 1 个皇后同列、同斜线,不用再判断是否同行,因为每行只放置一个,本来就已经不同行。
3 在第 3 行放置第 3 个皇后。第 3 个皇后的位置不能与前 2 个皇后同列、同斜线。
4 ......
5 在第 t 行放置第 t 个皇后。第 t 个皇后的位置不能与前 t-1 个皇后同列、同斜线。
6 ......
7 在第 n 行放置第 n 个皇后。第 n 个皇后的位置不能与前 n-1 个皇后同列、同斜线。
n 皇后问题的解的形式为 n 元组:{x1,x2,x3...xi...xn},分量 xi 表示第 i 个皇后放置在第 i 行第 xi 列,xi 的取值为 1,2...n。例如 x2=5,表示第 2 个皇后被放置为第 2 行第 5 列。
n 皇后问题的解空间是一棵 m(m=n) 叉树,树的深度为 n,如下图所示。
3.1 约束条件
在第 i 行放置第 t 个皇后时,第 t 个皇后的位置不能与前 t-1 个皇后同列、同斜线。第 i 个皇后与第 j 个皇后不同列,即 xi != xj,并且不同斜线 |i-j| != |xi-xj|。
3.2 限界条件
该问题不存在放置方案是否好坏的情况,所以不需要设置限界条件。
3.3 搜索过程
从根开始,以深度优先搜索的方式进行搜索。根节点是活节点并且是当前扩展节点。在搜索过程中,当前扩展节点沿纵深方向移向一个新节点,判断该新节点是否满足隐条件,如果满足,则新节点成为活节点,并且成为当前扩展节点,继续深一层的搜索,如果不满足,则换到该节点的兄弟节点继续搜索;如果新节点没有兄弟节点,或者其它兄弟节点已全部搜索完毕,则扩展节点称为死节点,搜索回溯到其父节点继续进行。搜索到问题的根节点变成死节点时为止。
在 4 * 4 的棋盘商放置 4 个皇后,使其不受攻击。
节点3是死节点,回溯到节点 2。
节点 5 是死节点,向上回溯到节点 4。
节点 4 是死节点,向上回溯到节点 2,节点 2 的所有孩子都考察完,也称为了死节点。向上回溯到节点 1。
t > n,找到一个可行解,用 bestx[] 保存当前可行解{2,1,4,3}。节点 9 称为死节点,向上回溯到节点 8。
不符合约束,节点 8 成为死节点,向上回溯到节点 7。
不符合约束,节点 7 成为死节点,向上回溯到节点 6。节点 6 的所有孩子都考察完,它也成为了死节点。向上回溯到节点 1。
t > n,找到一个可行解,用 bestx[] 保存当前可行解{3,1,4,2}。节点 13 称为死节点,向上回溯到节点 12。
不满足约束,成为死节点,向上回溯到节点 11,节点 11 的所有孩子都考察完,成为死节点。向上回溯到节点 10。
不满足约束,称为死节点,向上回溯到节点 1.
不满足约束,成为死节点,回溯到节点 15。
不满足约束,成为死节点,回溯到节点 14。
不满足约束,成为死节点,回溯到节点 14。回溯到节点 14。
不满足约束,成为死节点,回溯到节点 1。
在第 t 行放置第 t 个皇后时,第 t 个皇后与前 t-1 个皇后不能同列或同斜线。如果有一个成立,则第 t 个皇后不可以被放置在该位置。x[t] == x[j] 表示第 t 个皇后和第 j 个皇后同列。t-j == abs(x[t]-x[j]) 表示第 t 个皇后与第 j 个皇后同斜线。
t 表示当前扩展节点在第 t 层。如果 t>n,则表示已经到达叶子节点,记录最优值和最优解,返回。否则,分别判断 n 个分支,x[t]=i;判断每个分支是否满足约束条件,如果满足,则进入下一层,否则考察下一个分支(兄弟节点)。
package com.platform.modules.alg.alglib.p924;
public class P924 {
private final int M = 105;
// n表示n个皇后
private int n;
// x[i]表示第i个皇后放置在第i行第x[i]列
int x[] = new int[M];
// countn 表示 n 皇后问题可行解的个数
long countn;
public String output = "";
public String cal(String input) {
n = Integer.parseInt(input);
countn = 0;
Backtrack(1);
output += countn;
return output;
}
// 判断第 t 个皇后能否放置在第 i 个位置
boolean Place(int t) {
boolean ok = true;
for (int j = 1; j < t; j++) //判断该位置的皇后是否与前面t-1个已经放置的皇后冲突
{
// 判断列、对角线是否冲突
if (x[t] == x[j] || t - j == Math.abs(x[t] - x[j])) {
ok = false;
break;
}
}
return ok;
}
void Backtrack(int t) {
// 如果当前位置为 n,则表示已经找到了问题的一个解
if (t > n) {
countn++;
// 打印选择的路径
for (int i = 1; i <= n; i++)
output += x[i] + " ";
output += "\n";
} else
// 分别判断 n 个分支,特别注意 i 不要定义为全局变量,否则递归调用有问题
for (int i = 1; i <= n; i++) {
x[t] = i;
if (Place(t))
Backtrack(t + 1); // 如果不冲突的话进行下一行的搜索
}
}
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/chengqiuming/article/details/126320568
内容来源于网络,如有侵权,请联系作者删除!