N皇后问题----回溯法
2013-11-29 18:33
204 查看
N皇后问题描述为:如何能够在 n*n 的国际象棋棋盘上放置 n 个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。
比如经典的8皇后问题的一个解:
![](http://images.cnitblog.com/blog/527368/201311/29174954-8e9b473aed1e4fa9b35baa28fb10a1e1.png)
一开始你也许会像我一样,想到用一个二维数组来实现,并且用线面描述的算法:
用一个N*N的矩阵来存储棋盘:
1) 算法开始, 清空棋盘,当前行设为第一行,当前列设为第一列
2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步
3) 在当前位置上满足条件的情形:
在当前位置放一个皇后,若当前行是最后一行,记录一个解;
若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;
若当前行是最后一行,当前列不是最后一列,当前列设为下一列;
若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;
以上返回到第2步
4) 在当前位置上不满足条件的情形:
若当前列不是最后一列,当前列设为下一列,返回到第2步;
若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一 个待测位置,返回到第2步;
当然,实际上我们的思路就该如此,但真的用这样一个二维数组去实现的话会发生什么事情?我们需要在每一个递归中遍历同行,同列, 两条对角线,那么我们要调用多少次的判断函数?并且当N的数值很大的时候,二维数组很明显是低效的。那么我们应该如何去实现呢?
首先,我们已经知道不可能有两个皇后在同一行,又一共有 n 个皇后,所以每行有且只有一位皇后。另外,同理我们可以知道每列有且只有一位皇后。
有了这个前提,那么我们就可以直接用一个有 n 大小的一位数组来储存了,每个元素用来储存列号,如图:
![](http://images.cnitblog.com/blog/527368/201311/29181126-9ca6d98b1d23445783c59b166d9fa8e2.png)
OK,现在我们有了一种较为轻便的储存方式,剩下的就是怎么得到一个有效的八皇后序列问题了。我们都知道回溯法,这个是遍历所有可能情况以得到满足条件的序列,那么我们就可以遍历所有的可能序列判断满不满足,而得到全部的序列的方法就可以根据我们的储存结构的特点,也即一位数组,来得到全排列,然后判断每一个全排列满不满足条件。。。全排列可以看看我前面的关于全排列的博文。。。
那么我们应该怎么判断一个全排列是不是满足条件呢?因为我们的储存结构的特点,各行列上都只有一个皇后了,那么久只需要判断对角线了,我们来看看一个8*8的棋盘:
![](http://images.cnitblog.com/blog/527368/201311/29182648-928bfa82cba54d4da6fbcf82f371e066.png)
格子 [3 3] 和格子 [2 4] 我们可以发现 |3 - 2| == |3 - 4|,对 [3 5] 和 [4 4] 我们同样有 |3 - 4| == |5 - 4|,其他在同一对角线的元素都具有相同的性质,也就是说,在同一对角线上的元素会具有性质 |col_1 - col_2| == |row_1 - row_2|, 那么我们要判断两皇后是否在同一对角线,就只需要比较他们的行列值得对应差的绝对值了。
好了,到这里我们基本就可以得到N皇后问题的全部可能情况了,下面是实现的代码:
比如经典的8皇后问题的一个解:
![](http://images.cnitblog.com/blog/527368/201311/29174954-8e9b473aed1e4fa9b35baa28fb10a1e1.png)
一开始你也许会像我一样,想到用一个二维数组来实现,并且用线面描述的算法:
用一个N*N的矩阵来存储棋盘:
1) 算法开始, 清空棋盘,当前行设为第一行,当前列设为第一列
2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步
3) 在当前位置上满足条件的情形:
在当前位置放一个皇后,若当前行是最后一行,记录一个解;
若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;
若当前行是最后一行,当前列不是最后一列,当前列设为下一列;
若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;
以上返回到第2步
4) 在当前位置上不满足条件的情形:
若当前列不是最后一列,当前列设为下一列,返回到第2步;
若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一 个待测位置,返回到第2步;
当然,实际上我们的思路就该如此,但真的用这样一个二维数组去实现的话会发生什么事情?我们需要在每一个递归中遍历同行,同列, 两条对角线,那么我们要调用多少次的判断函数?并且当N的数值很大的时候,二维数组很明显是低效的。那么我们应该如何去实现呢?
首先,我们已经知道不可能有两个皇后在同一行,又一共有 n 个皇后,所以每行有且只有一位皇后。另外,同理我们可以知道每列有且只有一位皇后。
有了这个前提,那么我们就可以直接用一个有 n 大小的一位数组来储存了,每个元素用来储存列号,如图:
![](http://images.cnitblog.com/blog/527368/201311/29181126-9ca6d98b1d23445783c59b166d9fa8e2.png)
OK,现在我们有了一种较为轻便的储存方式,剩下的就是怎么得到一个有效的八皇后序列问题了。我们都知道回溯法,这个是遍历所有可能情况以得到满足条件的序列,那么我们就可以遍历所有的可能序列判断满不满足,而得到全部的序列的方法就可以根据我们的储存结构的特点,也即一位数组,来得到全排列,然后判断每一个全排列满不满足条件。。。全排列可以看看我前面的关于全排列的博文。。。
那么我们应该怎么判断一个全排列是不是满足条件呢?因为我们的储存结构的特点,各行列上都只有一个皇后了,那么久只需要判断对角线了,我们来看看一个8*8的棋盘:
![](http://images.cnitblog.com/blog/527368/201311/29182648-928bfa82cba54d4da6fbcf82f371e066.png)
格子 [3 3] 和格子 [2 4] 我们可以发现 |3 - 2| == |3 - 4|,对 [3 5] 和 [4 4] 我们同样有 |3 - 4| == |5 - 4|,其他在同一对角线的元素都具有相同的性质,也就是说,在同一对角线上的元素会具有性质 |col_1 - col_2| == |row_1 - row_2|, 那么我们要判断两皇后是否在同一对角线,就只需要比较他们的行列值得对应差的绝对值了。
好了,到这里我们基本就可以得到N皇后问题的全部可能情况了,下面是实现的代码:
#include <iostream> #include <stdio.h> #include <cmath> using namespace std; int count_; void output(int* queenArray, int numberOfQueen) { printf("|--------------------------------\n|"); for (int i = 0; i != numberOfQueen; i++) { for (int j = 0; j != numberOfQueen; j++) { if (j == queenArray[i]) printf(" $ |"); else printf(" * |"); } printf("\n|"); } printf("--------------------------------\n"); } void swap(int& a, int& b) { int temp = a; a = b; b = temp; } bool queenValuid(int* queenArray, int len) { //判断某序列是否有两个皇后在同一对角线 for (int i = 0; i != len; i++) { for (int j = i + 1; j != len; j++) { if (abs(j - i) == abs(queenArray[j] - queenArray[i])) return false; } } return true; } void queen(int* queenArray, int begin, int end) { if (begin == end) { if (queenValuid(queenArray, end + 1)) { count_++; output(queenArray, end + 1); } } else { //全排列 for (int i = begin; i <= end; i++) { swap(queenArray[begin], queenArray[i]); queen(queenArray, begin + 1, end); swap(queenArray[begin], queenArray[i]); } } } int main(int argc, char const *argv[]) { char command; int *queenArray; int numberOfQueen; do { int start = clock(); { count_ = 0; printf("Enter the number of the queen: "); scanf ("%d", &numberOfQueen); queenArray = new int[numberOfQueen]; for (int i = 0; i != numberOfQueen; i++) queenArray[i] = i; queen(queenArray, 0, numberOfQueen - 1); printf("Total queen seq: %d\n", count_); delete []queenArray; } printf("\nrun time: %.3lf ms\n",double(clock()-start)/CLOCKS_PER_SEC); printf("Again?<Y/N>\n"); scanf("\n%c", &command); } while (command == 'Y' || command == 'y'); return 0; }
相关文章推荐
- N 皇后问题 回溯/深搜
- Leetcode中的回溯法题目总结:八皇后问题; unique path问题;subsets问题
- 回溯算法 8皇后问题的一种解法 适合初学者观察整个回溯的过程
- [HDU 2553]--N皇后问题(回溯)/N皇后问题的分析
- hdu2553 N皇后问题(回溯dfs)
- hdu 2553 N皇后问题 dfs回溯
- 回溯算法n皇后问题
- N皇后问题---回溯法之一维数组存储
- 国际象棋“皇后”问题的回溯算法
- 国际象棋“皇后”问题的回溯算法
- 回溯算法之n皇后问题
- n皇后问题的两种方法:常规的回溯法和利用全排列的回溯法
- N皇后问题--递归回溯
- 回溯算法基础---皇后问题,骑士游历,迷宫求解
- 软件设计师算法之回溯法--8皇后问题
- “皇后”问题的回溯算法
- N 皇后问题 回溯/深搜
- 回溯法之经典问题->皇后问题
- poj 2533 N皇后问题 -- 递归回溯(打表)
- 回溯法的分析及N皇后问题