学算法第七篇--------DFS巧解八皇后问题

文章介绍了如何通过坐标表示法和树形结构来解决八皇后问题,利用对角线限制的全排列,通过递归和数组技巧避免重复,实现高效输出所有解法。
摘要由CSDN通过智能技术生成

                           

一、题目介绍

八皇后问题是指将 8个皇后放在 8 * 8 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个

皇后都不能处于同一行、同一列或同一斜线上.

现将皇后数量,棋盘长度以及棋盘宽度换为正整数n。列出所有可能保证任意皇后不被攻击。

输入格式

共一行,包含整数 n。

输出格式

每个解决方案占 n行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。其中 0表示某

一个位置的方格状态为空,1 表示某一个位置的方格上摆着皇后。


二、解决思路

本题也是借鉴了acwing当中y总的思路

突破点1:坐标表示皇后

既然是在一个n * n的带格子的地图上放置皇后,那么不妨将这些点以坐标形式呈现。

例如第一行第三列有一个皇后,我们可以将这个皇后位置设置为(0, 2),意为第一排第三列。

输出的时候直接判断该点是否为1,为1就代表有皇后,为0表示没皇后,输出出来一目了然。

突破点1的引申:树形结构的形成

假设棋盘大小是4 * 4

起初我们用四个空位置表示放入皇后的位置,例如2 4 1 3,就分别表示:皇后的位置出现在第1排第2列(0, 1),第2排第4列(1, 3),第3排第1列(2, 0), 第4排第3列(3, 2).

这个问题就变成一个带有对角线限制的全排列问题

由于从第一排开始到最后一排,每一列都会用1,2,3,4表示皇后在该行的第几列,因此我们用树形结构表示这一变化。横着代表列的变化,可以用for循环实现,纵向我们采用递归,来实现行的变化。(❌代表剪枝)

突破点2:对角线突破 

对角线分为正对角线和反对角线,如下图所示:

正对角线满公式:y = x + b            反对角线满足公式: y = -x + c(其中x是行,y对应列)

核心点:任意两个点不在对角线上意味着 行和列的和 或者 列减行的差 分别不为同一个常数。(y + x = c,y - x = b)。

从核心点开始引申:那么怎么如何在运行程序的时候保存住这个常数呢?总不能设置很多临时变量吧?

因此我们考虑设置数组来保存,只要是以这个常数为下标的数组值统统设置为true,数组的其他数据设置为false,例如(0,2)和(1, 1)肯定在一条对角线上(正对角线),因此列+行就是2,以2为下标的数组数据直接设置成true,若有x,y值满足则跳过。

// 定义正对角线,反对角线以及列.
bool dg[N * 2], udg[N * 2], col[N];

// N代表棋盘的长和宽
// i是列数,0就是第一列。cur是行数
for (int i = 0; i < N; i++) {
		if (!col[i] && !dg[i + cur] && !udg[i - cur + N]) {
			arr[cur][i] = 1;
			col[i] = dg[i + cur] = udg[i - cur + N] = true;
			dfs(cur + 1);
			arr[cur][i] = 0;
			col[i] = dg[i + cur] = udg[i - cur + N] = false;
		}
	}

易错点:当列数减去行数的时候,难免会出现负数的情况,而数组下标不可能为负数,因此我们统一加上棋盘的长度(即i - cur + N),因为最小的负数也是(3, 0),即第四排第一列,0-3 = -3,绝对值小于4.

全代码展示:

# include <iostream>
# include <vector>
using namespace std;

const int N = 4;
int arr[N][N];

// 定义对角线,写对角线以及列.
bool dg[N * 2], udg[N * 2], col[N];


void dfs(int cur) {
	if (cur == N) {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				cout << arr[i][j] << " ";
			}
			cout << endl;
		}
		cout << endl;
		return;
	}

	for (int i = 0; i < N; i++) {
		if (!col[i] && !dg[i + cur] && !udg[i - cur + N]) {
			arr[cur][i] = 1;
			col[i] = dg[i + cur] = udg[i - cur + N] = true;
			dfs(cur + 1);
			arr[cur][i] = 0;
			col[i] = dg[i + cur] = udg[i - cur + N] = false;
		}
	}
}

int main() {
	
	for (int i = 0; i < N;i++) {
		for (int j = 0;j<N; j++) {
			arr[i][j] = 0;
		}
	}

	
	dfs(0);
	

	return 0;
	

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值