n皇后问题思路分析即代码实现(有注释)

n皇后问题

题目:

在n * n的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之在同一行、同一列、同一斜线上的棋子。设计算法在n * n的期盼上放置n个皇后,使其彼此不受攻击。

思路分析:

核心思路: 以行为主导,去考虑每一行的皇后该放在哪一列,才能获得可行解。

n皇后问题解的形式: n元组 {x1, x2, … , xi , …, xn}, 分量xi表示第i行皇后放置的列位置(即i行xi列)

解空间组织结构: m叉树(m = n)

显约束(对解分量的取值范围的限定): 不同列(取值1 ~ n)

隐约束(能否得到问题的可行解或最优解做出的约束):

1)约束条件: 约束条件是对可行解做出的约束,在这里指第t个皇后的位置不能和前t - 1 个皇后同列、同斜线。

2)限界条件: 限界条件是对最优解做出的约束,该问题不是求最优值,只是求可行方案,因此不需要限界条件。

具体搜索过程:

​ 从根开始,以深度优先搜索的方式进行搜索。根节点是活结点,并且以该活结点为当前扩展结点。当前扩展结点沿纵深方向扩展出一个新结点,判断该结点是否满足约束条件。如果满足,则该新节点变成活结点,并且成为当前的扩展结点,继续向深层搜索;如果不满足约束条件,则该新结点变成死结点,然后换到该新结点的兄弟结点继续搜索,如果新结点没有兄弟结点,或其兄弟结点已经全部搜索完毕,则当前扩展结点变成死结点,搜索回溯到当前扩展结点的父结点继续进行。搜索过程直到根节点变成死结点为止,即搜索完毕。

实现约束条件的约数函数:

//约束函数(检查当前第x行皇后放好后,是否满足约束条件)
int place(int x){
	if(x == 1)return 1;	//第1行的皇后可以随便放
	for(int i = 1; i < x; i ++)	//检查当前x行的皇后是否与前x - 1行的皇后同列,同斜线
    {
		if(p[i] == p[x] || abs(x - i) == abs(p[x] - p[i]))return 0;	
	}
	return 1;
}
/*
1)检查当前位置与之前皇后位置是否处于同一列:p[i] == p[x]

2)检查当前位置与之前皇后位置是否处于同一斜线:abs(x - i) == abs(p[x] - p[i])
因为如果是在同一条斜线上,那么该两个皇后位置的水平差等于竖直差
*/

总概括: 通过一个数组的下标去表示皇后放置的行,数组值去表示皇后放置的列,从而我们可以从1开始递归每一行,然后去遍历当前行的每一列,判断是否可行,如果可行,那么就将该行的皇后放在该列,然后就去放下一行;如果当前行,每一列都不能放,那么就回溯到上一行去放其他的、可行的列位置。直到递归到第n + 1行,也就是说此时,我们前n行的皇后都放完了,即有一个可行方案,输出即可。

复杂度分析:

时间复杂度: O(n ^ (n + 1))

对于n皇后问题的解空间树是一颗n叉树,树的深度是n。最坏情况是所有的点都需要扩展,那么该n叉树所有的点数为1 + n + n^2 + …… + n ^ n = n^n ,每个点都要判断约束条件,即是否可以放,其时间复杂度为O(n),那么所有点都判断的时间复杂度是O(n ^ (n + 1))。对于可行的方案,我们需在叶子结点处输出可行方案,需要时间复杂度O(n),叶子结点数是n^n,因此时间复杂度是O(n ^ (n + 1)),综上,忽略常数,总的时间复杂度是O(n ^ (n + 1))

空间复杂度:O(n)

​ 回溯法的一个重要性质就是在搜索执行的同时产生解空间,在所搜索过程中的任何时刻,仅保留从开始结点到当前扩展结点的路径,从开始结点起最长路径就是数的深度为n,因此,该算法的空间复杂度为O(n)。

代码:

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n, flag = 0;	//flag 记录当前n皇后问题是否存在可行解
int p[20];	//记录n皇后的解, p[i] 表示第i行的皇后放在第[i]列

//约束函数(检查当前第x行皇后放好后,是否满足约束条件)
//判断第x个皇后能否放在第x行的第p[x]l列
int place(int x){
	if(x == 1)return 1;	//第1行的皇后可以随便放
	for(int i = 1; i < x; i ++)	//检查当前x行的皇后是否与前x - 1行的皇后同列,同斜线
    {
		if(p[i] == p[x] || abs(x - i) == abs(p[x] - p[i]))return 0;	
	}
	return 1;
}
//递归每一行
void dfs(int u){
    //递归到第n + 1 行,说明是一个可行解
	if(u == n + 1){
		flag = 1;
		for(int i = 1; i <= n; i ++)printf("%5d", p[i]);
		printf("\n");
		return;
	}	
    //枚举第u行的每一列
	for(int i = 1; i <= n; i ++){
		p[u] = i;	//将皇后放在第u行第i列
		if(place(u))	//如果该皇后可以放在该位置,就去递归下一行
        {
			dfs(u + 1);
		}	
	}
}
int main(){
    printf("请输入n皇后的个数n:");
	scanf("%d", &n);
	dfs(1);
	if(!flag)printf("no solute!\n");
	return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值