回溯法_N后问题

问题描述
在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。
7_3Z0P2XZFBCR_DRJ9`C_BQ.png

  • 定义问题的解空间
    • 解的形式:( x 1 , x 2 , … , x n x_1,x_2, … , x_n x1,x2,,xn)
    • x i x_i xi的取值范围: x i x_i xi=1,2, … ,n
    • 解空间是完全N叉树
  • 组织解空间
    • 满n叉树,树的深度为n
  • 搜索解空间
    • 约束条件:不同列且不处于同一正、反对角线上: ∣ i − j ∣ ≠ ∣ x i − x j ∣ |i-j|≠|x_i-x_j| ij=xixj
    • 限界条件:无
  • 搜索过程(以4皇后问题为例)运行结果如下, 两种解的方式:

0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 0 \begin{matrix} 0&1&0&0\\ 0&0&0&1\\ 1&0&0&0\\ 0&0&1&0\\ \end{matrix} 0010100000010100

0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 \begin{matrix} 0&0&1&0\\ 1&0&0&0\\ 0&0&0&1\\ 0&1&0&0\\ \end{matrix} 0100000110000010

详细代码如下:

#include <iostream>
#include <cstring>

using namespace std;

// 棋盘的大小
int n = 0;
// 棋盘,0表示有子,1表示无子
int** a = NULL;
// 解的序号
int cnt = 0;

// 约束函数 对于将要放置的点(x,y)进行判断能否放置
bool constraint(int x, int y) {
	// true 能放置 false不能放置
	bool flag = true;
	// 不能在同一列
	for (int i = 0; i < n; i++) {
		if (i != x && a[i][y] == 1) {
			flag = false;
		}
	}
	if (flag == false) {
		return false;
	}
	// 不能在从左上到右下的斜线
	for (int i = 1; i < n; i++) {
		int index_x1 = x - i;
		int index_y1 = y - i;
		int index_x2 = x + i;
		int index_y2 = y + i;
		if (index_x1 >= 0 && index_y1 >= 0 && a[index_x1][index_y1] == 1) {
			flag = false;
			break;
		}
		if (index_x2 < n && index_y2 < n && a[index_x2][index_y2] == 1) {
			flag = false;
			break;
		}
	}
	if (flag == false) {
		return false;
	}
	// 不能在从左下到右上的斜线
	for (int i = 1; i < n; i++) {
		int index_x1 = x - i;
		int index_y1 = y + i;
		int index_x2 = x + i;
		int index_y2 = y - i;
		if (index_x1 >= 0 && index_y1 < n && a[index_x1][index_y1] == 1) {
			flag = false;
			break;
		}
		if (index_x2 < n && index_y2 >= 0 && a[index_x2][index_y2] == 1) {
			flag = false;
			break;
		}
	}
	if (flag == false) {
		return false;
	}
	return true;
}

// 显示相应的棋盘信息
void display() {
	cout << "*************************************************" << endl;
	cout << "第" << ++cnt << "种方法" << endl;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cout << a[i][j] << " ";
		}
		cout << endl;
	}
	cout << "***************************************************" << endl;
}

// times代表的是当前的行号
void BackTrace(int times) {
	// 递归到最后一层了,打印输出
	if (times == n) {
		display();
		return;
	}
	// 遍历当前行的每一个点,查看是否满足约束函数
	for (int i = 0; i < n; i++) {
		if (constraint(times, i)) {
			a[times][i] = 1;
			BackTrace(times + 1);
			a[times][i] = 0;
		}
	}

}

int main() {
	cout << "请输入棋盘大小 : ";

	cin >> n;

	a = new int* [n];
	// 申请空间并一开始全部置0,表示没有放子
	for (int i = 0; i < n; i++) {
		a[i] = new int[n];
		memset(a[i], 0, sizeof(int) * n);
	}

	// 从第0行进行递归搜索
	BackTrace(0);

	// 一种解都没有,则证明无解
	if (cnt == 0) {
		cout << "矩阵维度为 " << n << " * " << n << "无解" << endl;
	}

	// 释放空间
	for (int i = 0; i < n; i++) {
		delete[]a[i];
	}
	delete[]a;
	
	return 0;
}

验证经典的 8 8 8皇后问题,运行结果如下:
Z`CD@63V__FF_7P_IV~@SCG.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

中小庸

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

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

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

打赏作者

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

抵扣说明:

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

余额充值