算法-八皇后问题
1、什么是八皇后问题?
八皇后问题是一个古老的问题,于1848年由一位国际象棋棋手提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,如何求解?
以高斯为代表的许多数学家先后研究过这个问题。后来,当计算机问世,通过计算机程序的运算可以轻松解出这个问题。
2、如何解决八皇后问题?
所谓递归回溯,本质上是一种枚举法。这种方法从棋盘的第一行开始尝试摆放第一个皇后,摆放成功后,递归一层,再遵循规则在棋盘第二行来摆放第二个皇后。如果当前位置无法摆放,则向右移动一格再次尝试,如果摆放成功,则继续递归一层,摆放第三个皇后......
如果某一层看遍了所有格子,都无法成功摆放,则回溯到上一个皇后,让上一个皇后右移一格,再进行递归。如果八个皇后都摆放完毕且符合规则,那么就得到了其中一种正确的解法。
说起来有些抽象,我们来看一看递归回溯的详细过程。
2.1.第一层递归,尝试在第一行摆放第一个皇后:
2.2.第二层递归,尝试在第二行摆放第二个皇后(前两格被第一个皇后封锁,只能落在第三格):
2.3.第三层递归,尝试在第三行摆放第三个皇后(前四格被第一第二个皇后封锁,只能落在第五格):
2.4.第四层递归,尝试在第四行摆放第四个皇后(第一格被第二个皇后封锁,只能落在第二格):
2.5.第五层递归,尝试在第五行摆放第五个皇后(前三格被前面的皇后封锁,只能落在第四格):
2.6.由于所有格子都“绿了”,第六行已经没办法摆放皇后,于是进行回溯,重新摆放第五个皇后到第八格。:
2.7.第六行仍然没有办法摆放皇后,第五行也已经尝试遍了,于是回溯到第四行,重新摆放第四个皇后到第七格。:
2.8.继续摆放第五个皇后,以此类推......
3、解决八皇后问题--Java实现
package 八皇后问题;
public class Point {
private int X;
private int Y;
public Point(int x, int y) {
this.X = x;
this.Y = y;
}
// set、get、有参构造
}
package 八皇后问题;
import java.util.ArrayList;
import java.util.List;
public class State {
private List<Point> pointList = new ArrayList<Point>();
private int lineNum;
// set、get
}
package 八皇后问题;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class EightQueen {
// 起始状态列表
public static List<State> startStates = new ArrayList<State>();
// 棋盘的行列数和要放置的皇后数量
public static final int lineNum = 8;
// 一个N×N的棋盘
public static Point[][] allPoints = new Point[lineNum][lineNum];
// 解法数量
public static int count = 0;
public static void main(String[] args) {
// 初始化棋盘
for (int i = 0; i < lineNum; i++) {
for (int j = 0; j < lineNum; j++) {
allPoints[i][j] = new Point(i, j);
}
}
// 初始化起始状态列表。每个State的PointList分别存放了第一行的8个坐标,并且设置第一行为遍历初始行
for (int i = 0; i < lineNum; i++) {
State state = new State();
state.getPointList().add(new Point(0, i));
state.setLineNum(0);
startStates.add(state);
}
// 对于初始化state列表中的每个state,进行遍历操作。
for (State state : startStates) {
calculate(state);
}
System.out.println("总数为:" + count);
}
public static void calculate(State state) {
Stack<State> stack = new Stack<State>();
stack.push(state);
while (!stack.isEmpty()) {
// 从stack里取出一个状态
State state2 = stack.pop();
// 如果已经遍历到最后一行,输出这个解
if (state2.getLineNum() == lineNum - 1) {
for (Point goalpoint : state2.getPointList()) {
for (int i = 0; i < lineNum; i++) {
if (i != goalpoint.getY())
System.out.print("_ ");
else
System.out.print("Q ");
}
System.out.println();
}
System.out.println();
count++;
continue;
}
// 否则寻找下一行可以放置皇后的位置
int currentLineNum = state2.getLineNum() + 1;
for (Point point : allPoints[currentLineNum]) {
// 如果该点可以放置皇后
if (isSatisfied(point, state2.getPointList())) {
// 创建一个state对象
State newState = new State();
// 把这个新的state的pointList设置为前一个点的pointList里的所有点加上当前的点的坐标
for (Point point2 : state2.getPointList()) {
newState.getPointList().add(new Point(point2.getX(), point2.getY()));
}
newState.getPointList().add(point);
// 设置新的state的行数为下一行
newState.setLineNum(currentLineNum);
// 入栈
stack.push(newState);
}
}
}
}
// 判断一个点是否可以放置皇后
public static boolean isSatisfied(Point point, List<Point> list) {
for (Point point2 : list) {
// 两个皇后不能再同一条横线、直线、斜线上。由于我们直接遍历的是下一行的点,所以肯定不会出现X坐标相同的情况
if (point2.getY() == point.getY()
|| Math.abs(point2.getX() - point.getX()) == Math.abs(point2.getY() - point.getY()))
return false;
}
return true;
}
}
每天努力一点,每天都在进步