您的位置:首页 > 理论基础 > 数据结构算法

n皇后

2015-10-30 09:13 471 查看
//一个很有研究意义的算法,借鉴贺老师的程序与讲解,珍惜这个资源,在此感谢老师  ^-^

/*在8×8的棋盘上摆放8个皇后,使其不能互相攻击,即任意的两个皇后不能处在同一行,同一列,或同一斜线上。可以把八皇后问题拓展为n皇后问题,

即在n×n的棋盘上摆放n个皇后,使其任意两个皇后都不能处于同一行、同一列或同一斜线上。

首先需要对棋盘进行描述。直观地,棋盘可以用二维数组表示,有皇后的棋格对应数组元素值为1,无皇后的棋格对应数组元素值为0。

但这种存储结构并不是最简单有效的选择。

图8.21中左边部分给棋盘的行、列编了号,提供的摆放方法,就是问题的一个解。

右边的部分,将各行上皇后所在的列数记录下来,用这8个数字(4, 6, 8, 2, 7, 1, 3, 5),也构成了对问题解的一种描述。



由此可以看出,可以定义一个一维数组int x
;,用x[i]的值表示第i行上皇后所在的列数,n皇后问题的解可以用(x[1], x[2], ….. x
)的形式描述。

解决了数据表示的问题,设计数据处理的方法。这里要用回溯的策略,设计计算机对n皇后问题的求解方法。以4皇后为例,

如图8.22所示,在图8.22(a)中,第1行第1列上放置一个皇后,图8.22(b)中确定第2行的可能放法,

在尝试第1列、第2列由于相互攻击而放弃之后,确定在第3列放置可以继续,在图8.22(c)中继续对第3行进行考察,

发现将所有4列都尝试过了,也没有办法将皇后安排一个合适的位置,对第4行做任何的尝试都没有意义,这时产生回溯,

结果是在图8.22(d)中将第2行的皇后安排到第4列,然后第3行的暂时可以放在第2列,

在图8.22(e)中试着确定第4行的皇后,却发现无解再次回溯,只能够如图8.22(f)所示将第1行的皇后放到第2列,

再经图8.22(g)、(f)之后找到4皇后问题的一个解,那就是图8.22(g)的(2, 4, 1, 3)。



在图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中两个加网格背景的结点。



*/

 

#include <stdio.h>
#include <math.h>
#include <malloc.h>

void nQueens(int *x, int n);            /*求解n皇后问题*/
int place(int *x, int k);               /*判断是否可以在第k行第x[k]列摆放皇后*/
void printSolution(int *x, int n);      /*输出求解结果*/

int main()
{
printf("欢迎探讨八皇后问题,请任意输入一个整数,以列出算法:");
int n;
int *x;                             /*存放求解结果的数组首地址*/
scanf("%d", &n);
x=(int*)malloc(sizeof(int)*(n+1));  /*动态分配数组空间, x[0]空闲*/
nQueens(x, n);
return 0;
}

/*如果一个皇后能放在第k行第x[k]列,则返回真(1),否则返回假(0)*/
int place(int *x, int k)
{
int i;
/*对前k-1行,逐行考察*/
for(i=1; i<k; i++)
{
/*如果前k-1行中有某行的皇后与第k行的在同一列或同一斜线,返回0*/
if((x[i]==x[k])||(fabs(x[i]-x[k])==fabs(i-k)))
return 0;
}
/*能执行下一句,说明在第k行第x[k]列摆放皇后,不会互相攻击*/
return 1;
}

/*求解在n×n的棋盘上,放置n个皇后,使其不能互相攻击*/
void nQueens(int *x, int n)
{
int k;
k = 1;        /*k是当前行*/
x[k] = 0;     /*x[k]是当前列,进到循环中,立刻就会执行x[k]++,而选择了第1列*/
while(k>0)    /*当将所有可能的解尝试完后,k将变为0,结束求解过程*/
{
x[k]++;                          /*移到下一列*/
while(x[k]<=n && !place(x, k))   /*逐列考察,找出能摆放皇后的列x[k]*/
x[k]++;
if(x[k]<=n)                      /*找到一个位置可以摆放皇后*/
{
if(k==n)                     /*是一个完整的解,输出解*/
printSolution(x, n);
else  /*没有完成最后一行的选择,是部分解,转向下一行*/
{
k++;     /*接着考察下一行*/
x[k]=0;  /*到循环开始执行x[k]++后,下一行将从第1列开始考察*/
}
}
else  /*对应x[k]>n的情形,这一行已经没有再试的必要,回溯到上一行*/
k--;  /*上一行在原第x[k]列的下1列开始考察*/
}
}

/*输出求解结果*/
void printSolution(int *x, int n)
{
int i, j;
for (i = 1; i <= n; i++)    /*输出第i行*/
{
for (j=1; j<=n; j++)
{
if (j == x[i])    /*第x[i]列输出Q,其他列输出*号 */
printf("Q");
else
printf("*");
}
printf("\n");
}
printf("\n");
}


关键:

定义一个一维数组int x
;,用x[i]的值表示第i行上皇后所在的列数,n皇后问题的解可以用(x[1], x[2], ….. x
)的形式描述。

解决了数据表示的问题,设计数据处理的方法。这里要用回溯的策略,设计计算机对n皇后问题的求解方法。以4皇后为例,

如图8.22所示,在图8.22(a)中,第1行第1列上放置一个皇后,图8.22(b)中确定第2行的可能放法,

在尝试第1列、第2列由于相互攻击而放弃之后,确定在第3列放置可以继续,在图8.22(c)中继续对第3行进行考察,

发现将所有4列都尝试过了,也没有办法将皇后安排一个合适的位置,对第4行做任何的尝试都没有意义,这时产生回溯,

结果是在图8.22(d)中将第2行的皇后安排到第4列,然后第3行的暂时可以放在第2列,

在图8.22(e)中试着确定第4行的皇后,却发现无解再次回溯,只能够如图8.22(f)所示将第1行的皇后放到第2列。

输出结果如下:



 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构