八皇后问题详细分析

流过昼夜 提交于 2020-02-01 15:09:17

八皇后问题是一个经典的算法,也是在面试中经常出现的题目。
最近也看到挺多的同学也在研究这个问题,写分享一下自己的理解吧!!!
题目描述的已经very very清楚了,就是一个皇后的8个方向都没有其他皇后,题目意思已经理解了,接下来怎么解决问题呢,怎么才能确保每一个皇后的8个方向没有其他皇后呢?
首先这个题目的解决办法肯定是暴力枚举所有情况(这里我们就得保证不能出现重复的情况),我们可以将第一行的皇后的位置先进行确定,然后再一行一行确定其他皇后的位置,接下来就是重点了,首先设置一个数组存每一列的状态(0:没有皇后,1:有皇后),再设置一个数组存左上到右下位置的状态,再设置一个数组存右上到左下的状态
行的状态已经确定好了不用管,列的状态直接存储就可以,那么其他四个方向的状态怎么存储呢???
大家不妨观察每一个位置对应的行和列(以一个6*6的为例)
在这里插入图片描述
先假定第一个皇后的位置位于(1,2),他的右下与左下就不能有其他皇后
左上到右下的坐标为(2,3)(3,4)(4,5)(5,6)(6,7)
右上到左下的坐标为(1,2)(0,3)(-1,4)(-2,5)(-3,6)
从而可以发现规律:(x为横坐标,i为纵坐标,n为棋盘大小)
左上到右下的坐标和为定值(即x+i)(注意:下标长度可能会超出设定棋盘大小,因此需要将数组开为棋盘数组大小的二倍)
同理右上到左下坐标和为定值,但是,要将值作为数组下标使用的,因此需要考虑差大于0,通过数学归纳法得出定值为横坐标的值-纵坐标的值+棋盘大小(即x-i+n)

到这里题目已经分析完了,代码应该就很好写了吧,dfs(就是递归)+回溯

回溯可能有些童鞋不知道,这里简单说一下它是干嘛的
当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。
由于我们这里每个皇后放置的位置不同,他的8个方向进行判断得到的值不一样,将以前的位置对应的8个方向的判断删除。所以需要用到回溯。
例如:一个4*4的棋盘,已经将2个皇后放到了如下位置
(1,1),(3,2)
此时我们该放第3个皇后了,但是发现都不符合条件了,这时,我们 就选择重新放置第2个皇后,将他之前判断的6个方向都删除(不用管行),即将判断列的数组下标为3的值变为0,同理将判断斜方向对应的数组的值变为0,重新放置皇后,再重新判断每个方向是否已有皇后,没有的话将第二个皇后放到该位置,然后再继续放置第三个;如果有的话就再回溯到第一个皇后,重新放置第一个皇后的位置。

接下来可以自己尝试的写一下了,还是不理解的话可以看一下下面的AC代码,你肯定会觉得怎么这么easy

#include<bits/stdc++.h>
using namespace std;
int n;
bool r[30],l[30],vis[15];//记录 /(右上到左下) \(左上到右下) 的状态 ,记录列的状态
int a[15];//用于记录皇后的位置,下标为行(1开始),值为列(1开始)
int ans;//记录排列的个数
//打印每种状态
void pr() {
	for(int i=1; i<=n; i++) {
		printf("%d ",a[i]);
	}
	puts("");
}
void dfs(int x) {
	if(x>n) {
		ans++;
		pr();
	} else {
		for(int i=1; i<=n; i++) {
			//当前位置没有棋子并且对角线也没有
			if(!r[i+x]&&!l[x-i+n]&&!vis[i]) {
				vis[i]=1;
				r[i+x]=1;
				l[x-i+n]=1;
				a[x]=i;
				dfs(x+1);
				vis[i]=0;
				r[x+i]=0;
				l[x-i+n]=0;
			}
		}
	}
}
int main() {
	scanf("%d",&n);
	dfs(1);
	printf("%d\n",ans);
	return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!