简单的8皇后问题。

来源:互联网 发布:动漫制作软件下载 编辑:程序博客网 时间:2024/04/30 12:26

目录:
1. 八皇后问题的递归解法。
2. 八皇后问题的非递归解法。 回溯法
3. 跳马问题的递归解法。

八皇后和跳马问题都属于AI(人工智能)范畴,这两个问题比较简单,属于计算机穷举。
而且这两个问题又都属于搜索问题。就是试探并记录下试探的步伐,最后输出正确的结果。
先分析一下八皇后问题。
题目一看就是递归,因为问题描述可以表示为在第k层放好的情况下,放第k+1个皇后,使与
前面的不相冲突。
好,现在分析第k+1层。
1. 若k+1层==9, 表示搜索已经结束,打印结果,返回。
2. 否则,在8个行上试探,若本行,无皇后,正对角线无皇后,斜对角线无皇后,在该处放置皇后。
3. 递归到下一层。

问题虽然描述清了,但还不能写代码,还要继续分析细化。
1. 打印结果,那么结果是什么 ? 我们可以用1个2维数组来表示棋盘,当放上皇后用1表示,不放
   皇后用0表示,很形象,对吗。打印的时候,你甚至可以打出图形来。
  
2. 怎样判断a[i][j]这个点能否放皇后,这是问题的关键。
   四个条件,本行,本列,对角线和反对角线
   可以简化为3个条件,把本列去掉,因为我们只在这列放一个。这列放不下,我们就回溯。我们
   还要在这1到8行上逐一试探。
   1. 要保证本列上无皇后。应该是调用一个函数。whether_this_row_has_queen(column). 返回布尔值。
     不过这个函数还是要查寻一个数组,如果这个数组元素值为1,返回true, 否则,返回false.
     这个数组,叫ROW_OCCUPY 数组。 8 个元素。 当本行放置皇后时,要置位该元素。
    
   2. 更复杂的是要保证对角线上无皇后。 它可以通过调用whether_reverse_diagonal_has_queen(row,column)
      先看反对角线吧。考察普通笛卡尔坐标下的棋盘。(0,0)第一条线
      (1,0)(0,1)第二条线 (2,0)(1,1)(0,2)第三条线...(7,0)..(0,7)第8条线
      row+column 是常数,继续(7,1)..(1,7)为第9条线,第(7,7)为第15条线。
      所以,我们可以用一个reverse_diagonal[15] 来表示,谁占用了,把相应的标志位置上。
     
   3. 正对角线。看函数whether_the_diagonal_has_queen(row,column)
      看(0,0)(1,1)。。(7,7)构成一条线。这是中间。row=column, row-column=0
      向两边扩散也有2n-1条对角线。左上(0,7),然后(0,6)(1,7)... 直到(6,0)(7,1)最后
      (7,0).row-column 从-7 变到0再到+7, 为防止序号出现负数,可以加上一个基数7.
      所以也可以设一个数组diagonal[15],占用了该对角线,
      在该数组置标志。
3. 递归到下一层。 简单,层数加一就可以了,这个层数,可以用行号代替。
补充,逐行放而非逐列放是调试方便

据以上分析,整理数据结构和逻辑关系,可以写出c代码程序如下:
 只有逻辑清楚,才能调试递归程序!!    
错误的疏漏,会影响程序的逻辑!  不调试的程序,full of mistake and wrong typewords etc.      
/* author: hjjdebug
 * date : 2009
 */
#include <stdio.h>
#include <stdlib.h>

int ans[8][8];
int col_occupy[8];
int diagonal_occupy[15];
int r_diagonal_occupy[15];
void queen(int row);

int main(void)
{
    queen(0);
    system("pause");
    return 0;
}
void queen(int row)
{
    int i,j;
    if(row==8)
    {
        char c;
        for(i=0;i<8;i++)
        { //print result (diagraph)
            for(j=0;j<8;j++)
            {
                printf("%c ",c=(ans[i][j]==1) ? '*':'-');
            }
            printf("/n");
        }
        printf("Quit(q) ? ");
        c=getchar();
        if(c=='q')
            exit(1);
        return ;
    }
    //try 8 position
    int column;
    for(column=0;column<8;column++)
    {// if this position is valid,hold it, recursive to next layer
        if(col_occupy[column]==0&&diagonal_occupy[7+column-row]==0&&r_diagonal_occupy[row+column]==0)
        {
            ans[row][column]=1;
            col_occupy[column]=1;
            diagonal_occupy[7+column-row]=1;
            r_diagonal_occupy[row+column]=1;
            queen(row+1);
            // back state to try next position
            ans[row][column]=0;
            col_occupy[column]=0;
            diagonal_occupy[7+column-row]=0;
            r_diagonal_occupy[row+column]=0;
        }
    }
    // when all try, return;
}

递归实际上属于深度优先搜索算法。可以利用函数栈存储数据和利用函数返回实现自动回退。
; --------------------------------------------------------------------------------------
递归是使用了函数栈,实际上是隐含回溯。 这里给一个不用递归用循环的程序,看其怎样实现回溯,如下例:
是的,非递归不如递归简洁。 但其进退之间别有一番情趣! 而且它还可以实现2级深度退却(依题意条件)。
还是迷宫退的深啊。
循环的退出条件是column 被逼回0,而不是通常的for 1 to 8 的结构,
有点新意, (有时间将详细介绍回溯的方法和思想) 下面程序不是很好,聊作参考吧。(有空再整理)
#include <stdio.h>
#include <stdlib.h>
#define MAXN 20
int good;
int ans[MAXN+1];
int a[MAXN+1];
int b[2*MAXN+1];
int    c[2*MAXN+1];
int main()
{
    int j;
    int column=1;
    int last_column=8;
    for (j=0;j<=last_column;j++) a[j]=1;
    for (j=0;j<=2*last_column;j++) b[j]=c[j]=1;
    ans[1]=1;good=1;ans[0]=0;
    do{
        if (good)
        {
            if (column==last_column)
            { // print result
                for (j=1;j<=last_column;j++)
                {
                    printf("%d ",ans[j]);
                }
                printf("/n");
                while (ans[column]==last_column)
                {
                    column--; /*清除关于第column列,ans[column]行有皇后标志*/
                    a[ans[column]]=b[column+ans[column]]=c[last_column+column-ans[column]]=1;
                }
                ans[column]++; /*调整第column列的皇后配置*/
            }
            else /*在第column列,ans[column]行位置设定有皇后标志*/
            {
                a[ans[column]]=b[column+ans[column]]=c[last_column+column-ans[column]]=0;
                ans[++column]=1; /*第column+1列,从第1行开始配置*/
            }
        }
        else
        {
            while (ans[column]==last_column)
            {
                column--; /*清除关于第column-1列、ans[column-1]行有皇后标志*/
                a[ans[column]]=b[column+ans[column]]=c[last_column+column-ans[column]]=1;
            }
            ans[column]++; /*调整第column列的皇后配置*/
        }
        good=a[ans[column]]&&b[column+ans[column]]&&c[last_column+column-ans[column]];
    } while (column!=0); //column 从第1行开始,最后被逼回0行,表示完全搜索结束。
    system("pause");
    return    0;

}