当前位置:   article > 正文

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

2. n皇后问题

一、n皇后问题介绍

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

例如:
**.*
.***
***.  这是一种4*4中的一种摆放方式,其中'*'代表没有放置,'.'代表
*.**  放置了皇后
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

二、解题思路

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

三、实现流程

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++,这样就找到了一种摆法,通过不断
重复这些操作,即可将所有情况找出来。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

四、代码实现

#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

五、改进方法

上面的方法是通过对每一行每一列依次判断的,并且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)的同一斜线是否有皇后,因为如果在同一斜线上,行数
之差等于列数之差

这样,我们就实现了优化,下面是代码:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
#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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

六、相关题目推荐

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

七、结语

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/633243
推荐阅读
相关标签
  

闽ICP备14008679号