n皇后问题的两种解题思路

文章介绍了n皇后问题的规则,即在n*n棋盘上放置n个皇后,不允许有相同行、列和对角线的皇后。通过回溯算法,从第一行开始逐列尝试放置,结合isValid函数检查是否合法。提供了两种代码实现,包括原始的二维数组和优化后的一维数组版本。文章还提及了相关题目以供练习,并强调了理解回溯思想的重要性。
摘要由CSDN通过智能技术生成

一、n皇后问题介绍

在一个n*n的棋盘放置n个皇后,使皇后彼此之间不能相互攻击,满足:
1、同一行只能有一个皇后 
2、同一列只能有一个皇后
3、每个皇后所在的主次对角线只有一个皇后,即它本身
共有多少种满足要求的摆法?

例如:
**.*
.***
***.  这是一种4*4中的一种摆放方式,其中'*'代表没有放置,'.'代表
*.**  放置了皇后

二、解题思路

可以从上往下依次摆放皇后,对于每一行,检查该行每一列是否满足摆放的
要求,如若满足,继续摆放下一行,直至每一行都有符合要求的皇后,得到
一种可行的摆放方案,然后通过回溯尝试不同的可能性。

三、实现流程

1.首先定义一个n*n的二维数组,为方便,可以将cheeboard[i][k]=0定义
成未放皇后,1为放置了皇后

2.然后调用backtracking函数,用for循环从第一行开始对每一列测试放皇
后,当然首先要满足那三个要求,这里可以用isValid函数对该位置能不能放
皇后进行判断,如若返回真,则将cheeboard[i][k]赋值为1,并调用
backtracking函数进行下一行皇后的摆放,最后回溯将cheeboard[i][k]
赋值为0,看看该行其余列是否可以放皇后

3.对于backtracking的返回条件,当然是当row(当前所在行) = n,即已经
将每一行都放置了皇后后结束,同时sum++,这样就找到了一种摆法,通过不断
重复这些操作,即可将所有情况找出来。

四、代码实现

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10;
int sum = 0; // 摆放的总数 
bool isValid(int chessboard[MAXN][MAXN], int n, int row, int col){
	for (int i = 0; i < row; i ++) {  // 判断是否同列,因为是从上往下一行一行来的,所以1到row-1即可 
		if (chessboard[i][col] == 1) // 且不用再判断是否是同一行了
			return false;
	}
	for (int i = row - 1, k = col - 1; i >= 0 && k >= 0; i --, k --) {
		if (chessboard[i][k] == 1) // 判断与主对角线平行的对角线,即左上的斜线 
			return false;
	}
	for (int i = row - 1, k = col + 1; i >= 0 && k < n; i --, k ++) {
		if (chessboard[i][k] == 1) // 判断与次对角线平行的对角线,即右上的斜线 
			return false;
	}
	return true;
}
void backtracking(int chessboard[MAXN][MAXN], int n, int row){
	if(row == n) { // 所有行都放了皇后 
		sum ++;
		return;
	}
	for (int i = 0; i < n; i ++) { // 对该行每一列测试放皇后 
		if (isValid(chessboard, n, row, i)) {
			chessboard[row][i] = 1;
			backtracking(chessboard, n, row + 1); // 进行下一行的放置 
			chessboard[row][i] = 0; // 回溯 
		}
	}
} 
int main() {
	int n;
  	cin >> n;
  	int chessboard[MAXN][MAXN] = {0};
  	backtracking(chessboard, n, 0);
  	cout << sum;
  	return 0;
}

五、改进方法

上面的方法是通过对每一行每一列依次判断的,并且isValid用了三个for
循环,因此会有点浪费时间,那么有没有什么可以优化的地方呢?答案是有
的。

我们可以将二维数组直接降为一维数组,通过对二维数组的定义,可以发现,
只有放置了皇后的才置为了1,其余都为0。那么实际上我们可以定义一个
result数组,其中result[i] = k表示第i行的第k列放置了皇后(假设
下标从0开始)

那么可以通过for循环对result[i]不同的赋值(0到n-1)实现在每一行的每一
列测试放置皇后,同时isValid函数中可以只用一个for循环解决,其中判断
条件为(result[i] = col || abs(row-i) == abs(col-result[i])),
前者判断该列是否已经有了皇后(result[i]存储的是第i行的皇后位置,后者
判断该位置(row,col)的同一斜线是否有皇后,因为如果在同一斜线上,行数
之差等于列数之差

这样,我们就实现了优化,下面是代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10;
int sum = 0; // 摆放的总数 
int result[MAXN] = {0};
bool isValid(int n, int row, int col){
	for (int i = 0; i < row; i ++) {  // 判断是否同列,因为是从上往下一行一行来的,所以1到row-1即可 
		if (result[i] == col || abs(row - i) == abs(col - result[i]))
			return false;
	}
	return true;
}
void backtracking(int n, int row){
	if(row == n) { // 所有行都放了皇后 
		sum ++;
		return;
	}
	for (int i = 0; i < n; i ++) { // 对该行每一列测试放皇后 
		if (isValid(n, row, i)) {
			result[row] = i; // 注意这里的写法,表示第row行第i列放置了皇后 
			backtracking(n, row + 1); // 进行下一行的放置 
			result[row] = 0; // 回溯 
		}
	}
} 
int main() {
	int n;
  	cin >> n;
  	backtracking(n, 0);
  	cout << sum;
  	return 0;
}

六、相关题目推荐

1.洛谷P1219 [USACO1.5]八皇后 Checker Challenge
2.力扣51–n皇后(该题需要知道一些vector的用法)
3.蓝桥杯官网–受伤的皇后

七、结语

每道题问的不一定一样,需要根据具体题目灵活变通,n皇后问题最重要的是理解其中的回溯思想。希望这篇文章有所帮助!qwq

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值