您的位置:首页 > 其它

N皇后问题----回溯法

2013-11-29 18:33 204 查看
N皇后问题描述为:如何能够在 n*n 的国际象棋棋盘上放置 n 个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

比如经典的8皇后问题的一个解:



一开始你也许会像我一样,想到用一个二维数组来实现,并且用线面描述的算法:

用一个N*N的矩阵来存储棋盘:

1) 算法开始, 清空棋盘,当前行设为第一行,当前列设为第一列

2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步

3) 在当前位置上满足条件的情形:

在当前位置放一个皇后,若当前行是最后一行,记录一个解;

若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;

若当前行是最后一行,当前列不是最后一列,当前列设为下一列;

若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;

以上返回到第2步

4) 在当前位置上不满足条件的情形:

若当前列不是最后一列,当前列设为下一列,返回到第2步;

若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一    个待测位置,返回到第2步;

当然,实际上我们的思路就该如此,但真的用这样一个二维数组去实现的话会发生什么事情?我们需要在每一个递归中遍历同行,同列, 两条对角线,那么我们要调用多少次的判断函数?并且当N的数值很大的时候,二维数组很明显是低效的。那么我们应该如何去实现呢?

首先,我们已经知道不可能有两个皇后在同一行,又一共有 n 个皇后,所以每行有且只有一位皇后。另外,同理我们可以知道每列有且只有一位皇后。

有了这个前提,那么我们就可以直接用一个有 n 大小的一位数组来储存了,每个元素用来储存列号,如图:


OK,现在我们有了一种较为轻便的储存方式,剩下的就是怎么得到一个有效的八皇后序列问题了。我们都知道回溯法,这个是遍历所有可能情况以得到满足条件的序列,那么我们就可以遍历所有的可能序列判断满不满足,而得到全部的序列的方法就可以根据我们的储存结构的特点,也即一位数组,来得到全排列,然后判断每一个全排列满不满足条件。。。全排列可以看看我前面的关于全排列的博文。。。

那么我们应该怎么判断一个全排列是不是满足条件呢?因为我们的储存结构的特点,各行列上都只有一个皇后了,那么久只需要判断对角线了,我们来看看一个8*8的棋盘:



格子 [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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: