C语言——八皇后编程

       1848年国际西洋棋棋手马克斯·贝塞尔提出八皇后问题:在8*8的国际象棋盘上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行,同一列或同一斜线,问有多少种摆法。

        首先我有以下几个问题需要解决:

        第一,国际象棋盘的存储实现;

        第二,应该用尝试和回溯的方法,一旦某一行失败,应回溯到上一行;

        第三,怎样判断一个位置是否安全。

        分析:对于棋盘的存储,我选择用8*8的二维数组实现。现在从第一行开始,要检测第一行第一个数据是否安全,若安全,设置此位置为皇后(数组值为1),然后处理第二行,处理完第二行之后要将第一行第一个数据值改为0,判断第一行第二个数据是否安全......在试了几次之后,从中找到规律:若此行没有安全的位置,则说明上一行摆放的位置不对,回到上一行。

        若处理到了第i行,则步骤如下:

        1、检测数组下标为[i][j]是否安全;不安全则转到5;

        2、安全,设置该位置为一个皇后,则下标[i][j] = 1;

        3、处理第i+1行;

        4、(处理完i+1行,此时递归处理完所有行),将本位置皇后去掉,[i][j]值为0;

        5、j++,转到1。

           开始编写程序,首先编写一个输出二维数组的函数,如下:

     void showEightQueens(int(*eq) [RANGE])
  {
        int i;
        int j;
         
        for(i = 0; i < RANGE; i++)
       {
        	for(j = 0; j < RANGE; j++)
             	 printf("%3d",eq[i][j]);
         printf("\n");
       }
   }


            以下程序为判断某位置是否安全:


    int isPositionSafe(int(*eq)[RANGE], int row, int col)//判断位置是否安全
 {
        int OK = TRUE;
        int i;
        int j;
        for(i = row - 1, j = col - 1; OK && i >= 0 && j >= 0; i--, j--)
            	if(eq[i][j] == 1)
                 OK = FALSE;
        for(i = row - 1, j = col; OK && i >= 0; i--)
  		if(eq[i][j] == 1)
   		OK = FALSE;
 	for(i = row - 1, j = col + 1; OK && i >= 0 && j < RANGE; i--, j++)
  		if(eq[i][j] == 1)
   		OK = FALSE;
 	return OK;
}


         最主要的递归函数来了:

    void eightQueens(int (*eq)[RANGE], int i)
{
 	int j;
 	if(i >= RANGE)
  		showEightQueens(eq);
	else
 	{
  		for(j = 0; j < RANGE; j ++)
  		{
   			if(isPositionSafe(eq, i, j))//判断此位置是否安全
   			{
    				eq[i][j] = 1;//若安全将此位置放皇后
    				eightQueens(eq, i + 1);//处理下一行
    				eq[i][j] = 0;//完成处理行的操作后,将皇后去掉。因为要列出所有的可能结果,则应该每个位置都判断
   			}
  		}
 	}
}


       编写一个递归函数时,必须考虑函数的出口。以上函数出口为:若行数等于RANGE,那么说明所有的行已经处理完毕,可以输出二维数组。用递归程序一直不熟练,不知道什么时候开始递归,递归出口,什么时候递归完成。曾经为了上台讲汉诺塔程序,用到了一种纸片的方法完成汉诺塔4个盘子的递归,方法是这样的:每个纸片上注明本次执行的函数,要调用的函数,为纸片编上序号,然后按从大到小的序号摞起来(编号小的在下面),我们很清晰的看到整个调用函数以及返回的过程。这是一个很有用的方法,对递归过程不清楚的可以采用纸片方法。
       若想要知道八棋盘问题有多少种摆法,我们可以通过计算出共调用了多少次showEightQueens()函数来得出。在showEightQueens()加入static变量实现此功能。以下为完成的八皇后问题程序。

#include
#include
#define  RANGE 8
#define  TRUE 1
#define  FALSE 0
void showEightQueens(int(*eq) [RANGE]);
int isPositionSafe(int (*eq)[RANGE], int row, int col);
void eightQueens(int (*eq)[RANGE], int i);
void eightQueens(int (*eq)[RANGE], int i)
{
 	int j;
 	if(i >= RANGE)//递归函数必须要有一个出口,不能无限递归
  		showEightQueens(eq);
	else
 	{
  		for(j = 0; j < RANGE; j ++)
  		{
   			if(isPositionSafe(eq, i, j))//判断此位置是否安全
   			{
    				eq[i][j] = 1;//若安全将此位置放皇后
    				eightQueens(eq, i + 1);//处理下一行如果下一行所有位置都不安全,则返回调用递归的位置,继续进行下面的操作。
    				eq[i][j] = 0;//完成此位置的操作后,将皇后去掉。因为要列出所有的可能结果,则应该每个位置都判断。
   			}
  		}
 	}
}
int isPositionSafe(int(*eq)[RANGE], int row, int col)//判断位置是否安全
{
 	int OK = TRUE;//这是一个技巧,我应该学会,相当于计算机用到很多的标志位
 	int i;
 	int j;
 	for(i = row - 1, j = col - 1; OK && i >= 0 && j >= 0; i--, j--)
  		if(eq[i][j] == 1)
   			OK = FALSE;
 	for(i = row - 1, j = col; OK && i >= 0; i--)
  		if(eq[i][j] == 1)
   			OK = FALSE;
 	for(i = row - 1, j = col + 1; OK && i >= 0 && j < RANGE; i--, j++)
  		if(eq[i][j] == 1)
   			OK = FALSE;
 return OK;
}
void showEightQueens(int(*eq) [RANGE])
{
 	int i;
 	int j;
 	static int count = 0;//用到了静态变量
 	printf("第%d种摆法\n", ++count);
 	for(i = 0; i < RANGE; i++)
 	{
  		for(j = 0; j < RANGE; j++)
   		printf("%3d",eq[i][j]);
  		printf("\n");
 	}
}
void main(void)
{
 	int eightQueen[RANGE][RANGE] = {0};
 	eightQueens(eightQueen, 0);
 	getch();
}

运行结果为92种摆法。

你可能感兴趣的:(C语言——八皇后编程)