读完本文这个小故事,相信你会完全弄懂N皇后问题。
故事还得从中国象棋说起…
中国象棋 — 国王背后的女人
中国象棋中将特别重要,一旦将被吃,则满盘皆输。这里我们暂且把将称为国王吧,国王虽然如此重要,但只能在田字格里移动,而且每次只能移动一格,尽管可以往横线和斜线的方向移动。
和他背后的女人 — 皇后相比,国王简直弱到不行:
在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行、列和斜线的敌方棋子。这简直比她没用的老公国王厉害不知多少倍。
国王的烦恼 — 安排皇后
然鹅现实就是这么令人啼笑皆非,一个国王可以有多个(N
)皇后,但一个皇后只能有一个国王。
国王的烦恼来了,如何将这N
个夫人安置在后宫里,且让她们每个人都能和睦相处(不会有吃掉的情况发生)。
于是乎,国王开始了他的思考过程:
安排在一间屋子肯定是不行了,他们在战场上都可以吃掉与她们处在同一行,同一列和同一斜线的敌人,这样的话给她们住一排屋看样子也不行了。
算了,让小兵们给N
个夫人们建造一个N*N
个屋子吧,就排成正方形,让她们每个人住一排吧,以后我就只能在这一个个田字组成的后宫里走来走去看望夫人们了。(国王果然还是喜欢走田字)
过了不久的时间,手下告诉国王后宫建好了。于是,国王开始了安置夫人们的旅程。
国王的后宫 — N皇后
这里我们以4
皇后的问题来举例说明
-
一开始后宫是空的,对于第一个皇后,随便挑:
-
但是国王也讲究先来后到,于是国王把第
1
个皇后安排在了坐标为(1,1
)的屋子里,这样一样,第1
行和第1
列里都不能住别的皇后了。对于第2
行,第3
列和第4
列均可以放皇后:
-
于是国王叫来了第
2
位皇后,把她放在了第2
行第3
列,但是国王发现这样一来,第3
行就不能放皇后了,于是国王立马让第2
位皇后住在了第2
行第4
个屋:
-
聪明的你立马看出来了,第
4
行已经不能住任何皇后了,因为第3
个皇后和第4
个皇后的屋子处在一个斜线上。当然,国王也不傻,于是他赶紧让第2
个皇后暂时先搬出来,并告诉皇后这个屋子风水不好,她不能住。皇后虽然很厉害,但也很听国王的话,于是第2
个皇后立马搬了出来。后来,国王让第1
个皇后住在了第1
行第2
列的屋子,然后国王也在考虑第2
为皇后住哪的问题(第2
行第4
列绿色):
-
然后国王让第
2
位皇后住进了第2
行第4
列的屋子,同时国王已经在考虑第3
位皇后的住宿问题了(第3
行第1
列绿色):
-
国王让第
3
位皇后,住在了第3
行第1
列的屋子,这时国王开始高兴了起来,因为国王发现第4
位皇后的屋子也有了着落(第4
行第3
列绿色)
-
于是国王在将第
3
位皇后安排好后立马让第4
为皇后住在了第4
行第3
列的屋子。国王的烦恼终于解决了,最终这4
位皇后的屋子位置情况如下:
国王的心事终于得到解决,于是便和皇后们没羞没臊了起来,此处略去不知多少字…
试问谁又不喜欢尝试新鲜呢?不知过了多少个日子,国王突然想给皇后们换个屋子,于是他又尝试了起来(这里不再赘述细节)
最终皇后们的屋子位置情况如下:
一句话概括N皇后问题
事实上,以上问题就是N
皇后问题,在N*N
的格子里放下N
个皇后,并求解有多少中解法
4
皇后问题只有以上2
中解法
回溯法
国王在安排皇后们时不断尝试,如果在这一步发现不能解决问题,就回过头修改上一步的策略的问题其实就是回溯法
因此,回溯法徒有其名,剥去其华丽的外衣,它的本质是一种试探的方法:
对于某个问题的求解,先将当前的选择作为解的一部分,基于当前的解法继续求解,当试探到某一步时,发现原先的选择并不能达到目标,则退回去修改原来的选择。
这种走不通就退回再走的方法称为回溯法。
N皇后问题的回溯法C++求解
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
int N,res=0;
int queen(int *arr,int r){
if(r==N)
res++;
else{
for(int c=0;c<N;c++){
arr[r]=c;
bool can=true;//用于标记是否可以放皇后
for(int c1=0;c1<r;c1++){ //不在同一行
if(arr[r]==arr[c1]|| //同一列的情况
abs(r-c1)==abs(arr[r]-arr[c1])){ //同一对角线的情况
can=false;
break;
}
}
if(can)
queen(arr,r+1);
}
}
return res;
}
int main(){
N=4;
int arr[N];
cout<<queen(arr,0);
return 0;
}
以上代码运行结果为:
2
才疏学浅,难免有错误和不当之处,欢迎交流批评指正!
同时有问题的话欢迎留言或邮箱联系(ljt_IT@163.com)。