问题描述:国际象棋棋手马克斯于1848年提出:在8*8的国际象棋放八个皇后,使其不能相互攻击,即任意两个皇后不能处于同一行,同一列,或者同一斜线上。(不知道为啥的朋友可以去了解以下国际象棋的基本规则),高斯认为有76种解法,计算机出来后解出92种解法。(终于在数学上可以超过一下天才儿童高斯了o(^▽^)o)
主要算法:回溯
算法详解:使用一个一维数组queue[8],因为一行只用放置一个皇后,所以我们可以用queue[i]的值来存储这一行放置的列数,而i表示行数,这样我们只需要一个一维数组就可以表示出8行棋盘每行只放置一个皇后的状态。算法的原理主要是:
1.从第1行开始的第一个位置开始放置;
2.判断此时放置的位置是否可以放置;(设置一个函数判断是否可以放置)
3.若判断可以放置那么从下一行的第一列开始设置下一行。若是行数已经到了8行,那么打印所有的放置位置。若此时的放置位置不可以放置,那么更改此时放置的位置。(此时,采用回溯算法,若可以放置则到下一行,不可以放置则返回到上一行。若是不能理解,先去了解递归时的系统栈)
4.打印出结果
函数详解:
check(int n)//check函数来表示当前位置是否可以放置,n表示当前的放置行数。其主要内容如下:
for(i=0;i<n;i++)
{
if(queue[i]==queue[n]||abs(queue[i]-queue[n])==(n-i))
{
return 0;
}
}
return 1;
for循环表示从第1行开始直到现在放置的这一行来检查是否符合放置规则。因为i和n不相同,所以不需要检查在同一行的情况。
只需要检查是否在同一列或者在同一斜线的情况。queue[i]==queue[n]就表示在同一列上有两个皇后了,注意queue[i]和queue[n]的值代表列,所以他们两的值不可以相同。对于斜线的检查,数学原理为|y1-y2|==|x1-x2|。在我们设置的变量中queue[i]和queue[n]的值就相当于x1和x2。i和n就相当于y1和y2.abs是绝对值函数。
put(int n)//放置函数,其内容如下:
int i=0;
for(i=0;i<max;i++)
{
queue[n]=i;
if(check(n)){
if(n==max-1)
show();
else
put(n+1);
}
}
max可在宏定义中定义其值为8。由第1行开始放置,并且判断是否可以放置,之后进入回溯。满8行后打印即可。
完整程序如下,已经在codeblock上运行过了:
#include<stdio.h>
#define max 8
int queue[max];
int sum=0;
void show()
{
int i=0;
for(i=0;i<max;i++){
printf("(%d,%d)",i,queue[i]);
}
sum++;
printf("\n");
}
int check(int n)
{
int i=0;
for(i=0;i<n;i++)
{
if(queue[i]==queue[n]||abs(queue[i]-queue[n])==(n-i))
{
return 0;
}
}
return 1;
}
void put(int n)
{
int i=0;
for(i=0;i<max;i++)
{
queue[n]=i;
if(check(n)){
if(n==max-1)
show();
else
put(n+1);
}
}
}
void main()
{
put(0);
printf("\n%d",sum);
}