算法-八皇后问题

算法-八皇后问题

 

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;
	}

}

 

 

每天努力一点,每天都在进步

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

powerfuler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值