N皇后问题的非递归回溯和递归回溯
2017-02-26 16:14
519 查看
一、问题描述
一个n*n的棋盘,要在上面放n个皇后。规则:两个皇后之间如果是同列、同行、同对角线它们会互相攻击。也就是说:棋盘上的任意两个皇后皇后不能为同列、同行、同对角线。
二、问题分析
回溯法是一种通用的搜索算法,几乎可以用于求解任何可计算的问题。算法的执行过程就像是在迷宫中搜索一条通往出口的路线,总是沿着某一方向向前试探,若能走通,则继续向前进;如果走不通,则要做上标记,换一个方向再继续试探,直到得出问题的解,或者所有的可能都试探过为止。首先需要对棋盘进行描述。直观地,棋盘可以用二维数组表示,有皇后的棋格对应数组元素值为1,无皇后的棋格对应数组元素值为0。但这种存储结构并不是最简单有效的选择。
下图中左边部分给棋盘的行、列编了号,提供的摆放方法,就是问题的一个解。右边的部分,将各行上皇后所在的列数记录下来,用这8个数字(4, 6, 8, 2, 7, 1, 3, 5),也构成了对问题解的一种描述。
由此可以看出,可以定义一个一维数组int x
;,用x[i]的值表示第i行上皇后所在的列数,n皇后问题的解可以用(x[1],
x[2], ….. x
)的形式描述。
解决了数据表示的问题,设计数据处理的方法。这里要用回溯的策略,设计计算机对n皇后问题的求解方法。以4皇后为例,如下图所示,在图8.(a)中,第1行第1列上放置一个皇后,图(b)中确定第2行的可能放法,在尝试第1列、第2列由于相互攻击而放弃之后,确定在第3列放置可以继续,在图(c)中继续对第3行进行考察,发现将所有4列都尝试过了,也没有办法将皇后安排一个合适的位置,对第4行做任何的尝试都没有意义,这时产生回溯,结果是在图(d)中将第2行的皇后安排到第4列,然后第3行的暂时可以放在第2列,在图(e)中试着确定第4行的皇后,却发现无解再次回溯,只能够如图(f)所示将第1行的皇后放到第2列,再经图(g)、(f)之后找到4皇后问题的一个解,那就是图(g)的(2,
4, 1, 3)。
图8.22 用回溯找出4皇后问题一个解的过程
在图8.23中,给出了求出4皇后问题所有解的完整过程的描述。图中(1
* * *)对应图8.22(a)中第1行皇后安排在第1列,其他行待定的状态,接下来的(1 3 * *)对应了图8.22(b)中第2行皇后安排在第3列的状态。可以判断出在这个状态下,继续尝试并不能够完成求解,于是发生回溯(其下方的B代表回溯),于是下一个尝试的状态将是(1 4 * *),……。将这样的过程继续下去,能够找出4皇后问题的所有解(2 4 1 3)和(3 1 4 2),如图8.23中两个加网格背景的结点。
图8.23 求出4皇后问题所有解的完整过程
搞清楚用回溯法求解的过程后,将关注如何基于(x[1],
x[2], ….. x
)形式的解结构,写出让计算机完成求解过程的代码。4皇后问题尚且可以在纸上画出解,8皇后问题的可能解有8!=40320种,最终解有92种,必须要依靠计算机求解了。
什么样的解才是可行的?需要描述出任何两个皇后可以“互相攻击”这样的条件:
(1)有两个皇后处在同一行:解的结构(x[1], x[2], ….. x
)已经保证同一行不会出现两个皇后。
(2)有两个皇后处在同一列:表示为x[i]=x[k],假如在图8.23中出现表示为(1 1 * *)、(4 2 3 2)之类的结点,则说明有两个皇后在同一列了。
(3)有两个皇后处在同一斜线:若两个皇后的摆放位置分别是第i行第x[i]列、第k行第x[k]列,若他们在棋盘上斜率为-1的斜线上,满足条件i-x[i]=k-x[k],例如(1 4 3 *)、(4 1 2 *);若他们在棋盘上斜率为1的斜线上,满足条件i+x[i]=k+x[k]。将这两个式子分别变换成i-k=x[i]-x[k]和i-k=x[k]-x[i],例如(3
4 1 *)。综合两种情况,两个皇后位于同一斜线上表示为|i-k|=|x[i]-x[k]|。
在下面的程序实现中,place(x, k)函数用于判断在第k行第x[k]列放置皇后,是否会与前面摆放好的皇后产生相互攻击。只要有某行(第i行)的皇后与这个第k行的皇后处在同一列(x[i]=x[k])或者处在同一斜线(|i-k|=|x[i]-x[k]|),则立即返回假(0),表示不可能构成解。
再接下来,就是在实现问题求解的nQueens(x, n)函数中,从第1行开始,逐行逐列地考察皇后的摆放,当遇到某一行所有可能情况试过不必再深入到下一行考察时,及时回溯到上一行,接着考察。
程序实现中,将保存解的数组定义成了动态数组。多分配一个单元,因为数组的首元素x[0]一直空闲未用,有用的单元是x[1]到x
。
根据以上分析,这个问题可以采用两种方法解决,一种是非递归回溯,另一种是递归回溯。详细请看以下代码:
/* 改程序采用了两种方案解决N皇后问题; 一种是采用非递归的回溯算法,第二种 是采用递归的回溯算法。 */ #include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> using namespace std; //声明各个函数 void nqueen1(int *a, int n); //非递归的方法 void nqueen2(int *a, int row, int n); //递归的方法 bool valid(int *a, int k); //判断在第k行,a[k]的皇后是否有效 void printqueen(int *a, int n); //打印输出皇后位置 int main() { int n; cout << "请输入皇后的个数n:" << endl; cin >> n; int *a; //存放结果的数组的首地址 a = (int*)malloc(sizeof(int)*(n + 1)); //动态分配数组空间,a[0]空闲; cout << "非递归回溯输出解决方案:" << endl; nqueen1(a, n); cout << "递归回溯输出解决方案:" << endl; nqueen2(a, 1, n); } /*如果一个皇后能放在第k行第a[k]列,则返回真(1),否则返回假(0)*/ bool valid(int *a, int k) { int i; for (i = 1; i < k; ++i) { /*如果前k-1行中有某行的皇后与第k行的在同一列或同一斜线,返回0*/ if ((a[i] == a[k]) || (abs(i - k) ==abs(a[i] - a[k]))) return false; } return true; } void printqueen(int *a, int n) { int i, j; for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { if (a[i] == j) cout << "Q"; else cout << "*"; } cout << endl; } cout << endl; } void nqueen1(int *a, int n) { int count = 0; int k; k = 1; //k a[k] = 0;/*a[k]是当前列,进到循环中,立刻就会执行a[k]++,而选择了第1列*/ while (k > 0) { a[k]++; /*移到下一列*/ while (a[k] <= n && !valid(a,k)) /*逐列考察,找出能摆放皇后的列a[k]*/ a[k]++; if (a[k] <= n)/*找到一个位置可以摆放皇后*/ { if (k== n)/*是一个完整的解,输出解*/ { count++; cout << "这是非递归第" << count << "个解法。" << endl; printqueen(a, n); //此时k==n,再进入循环,考察第k行的下一列 } else/*没有完成最后一行的选择,是部分解,转向下一行*/ { k++; /*接着考察下一行*/ a[k] = 0;/*到循环开始执行a[k]++后,下一行将从第1列开始考察*/ } } else { /*对应a[k]>n的情形,这一行已经没有再试的必要,回溯到上一行*/ k--; }/*上一行在原第a[k]列的下1列开始考察*/ } } int countbacak=0; void nqueen2(int *a,int row, int n) { if (row > n) //此时调用开始返回 { countbacak++; cout << "这是递归第" << countbacak << "个解法。" << endl; printqueen(a, n); } else { for (int i = 1; i <= n; ++i) //i表示列,每个i,都对应一系列的递归调用 { a[row] = i; //形成坐标 if(valid(a,row)) nqueen2(a,row+1, n); } } }
参考:http://blog.csdn.net/sxhelijian/article/details/48914323
相关文章推荐
- 回溯法解决N皇后问题——递归与非递归求解
- UVa 639 - Don't Get Rooked 类皇后问题 递归回溯
- 九度OJ 1254:N皇后问题 (N皇后问题、递归、回溯)
- 【蓝桥杯-递归回溯】八皇后问题+N皇后问题
- 递归回溯问题的四道经典题:N皇后,组合,全排列,二叉树路径和
- HDOJ/HDU 2553 N皇后问题 回溯加递归
- N皇后问题(回溯递归)
- N皇后问题 --递归及回溯解决方案
- 九度OJ 1254:N皇后问题 (N皇后问题、递归、回溯)
- 回溯法求n皇后问题(递归、非递归及优化)
- 用递归的方法和非递归方法解决8皇后问题
- 杭电 2553 N皇后问题 递归回溯 打表 附解题思路
- N皇后问题--递归回溯
- NQueens, NQueens2 N皇后问题,递归回溯
- 树的递归回溯 n皇后问题
- 用递归的方法和非递归方法解决8皇后问题
- N皇后问题的递归回溯实现
- 蓝桥杯2n皇后问题(简单递归回溯)
- N皇后问题 递归回溯
- 递归与回溯 HDOJ 2553 N皇后问题 1016 Prime Ring Problem