回溯法解决八皇后问题

 
说明西洋棋中的皇后可以直线前进,吃掉遇到的所有棋子,如果棋盘上有八个皇后,则这八个皇后如何相安无事的放置在棋盘上,1970年与1971年, E.W.Dijkstra与N.Wirth曾经用这个问题来讲解程式设计之技巧。
图片

 

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

解法关于棋盘的问题,都可以用递回求解,然而如何减少递回的次数?在八个皇后的问题中,不必要所有的格子都检查过,例如若某列检查过,该该列的其它格子就不用再检查了,这个方法称为分支修剪。

仔细观察从右上到左下的斜着的列i+j(行+列)的值是2-----9-----16 ,即范围是2~16,这15列可以用数组的元素表示

图片

从左下到右上的斜着的列i-j(行+列)的值是0----- (-7)与 0-----7,即范围是 -7~7,数组索引不能是负数所以加上一个N来使其范围变成 1~15,同样用数组表示这一列

图片

其实上面的两个斜着的列的i与j之间的关系是可以推导出来的,证法如下:

取右上到左下的为例,还记得高中时学的直线的斜率吗?

斜率K=(y1-y2)/(x1-x2)=tan45°=1,所以:x1-x2=y1-y2,即:x1-y1=x2-y2,由于我们选取的坐标系Y轴方向与笛卡尔坐标系反向,所以x1+y1=x2+y2。同理可得到:左下到右上的时候x1-y1=x2-y2 , 这就是行与列坐标之间的关系。

至于竖着的列很容易理解,直接用一个数组表示,因为是先放置上一行后才放置下一行,所以行不会重复。
好了,下面是代码:

 1 /*
 2     *******************************************************************************
 3     时间  :2014年9月15日 14:10:04
 4     程序名:八皇后问题.c
 5     By    :xxNote
 6     *******************************************************************************
 7 */
 8 #include <stdio.h>
 9 
10 #define N 8
11 int Queen[N+1];//皇后放在哪一列,数组索引从1~N表示从第1行到第N行,Queen[0]没使用,数组元素的值表示相应的列
12 int Col[N+1];//哪一列可以放置皇后,1表示可以放置,同样,第一个数组元素不使用了,Col[1]~Col[N]表示第1~N列
13 int Rtl[2*N+1];//右上到左下的斜着的列,哪一列可以放置皇后,Rtl[2]~Rtl[2*N] 表示 i+j=2~16对应的列,Rtl[0]和Rtl[1]没使用
14 int Ltr[2*N+1];//左上到右下的斜着的列,哪一列可以放置皇后,Ltr[1]~Ltr[2*N-1] 表示 i-j+N=1~15对应的列, Ltr[0]和Ltr[2N]没使用
15 int Num;//第几个解法
16 void Place(int i);//放置皇后
17 void Show(void);//显示放置的方法
18 int main(void)
19 {
20     int i;
21 
22     for (i=1; i<=N; i++)
23     {
24         Col[i] = 1;
25     }
26 
27     for (i=2; i<=2*N; i++)
28     {
29         Rtl[i] = 1;
30     }
31 
32     for (i=1; i<2*N; i++)
33     {
34         Ltr[i] = 1;
35     }
36 
37     Place(1);
38 
39     return 0;
40 }
41 
42 void Place(int i)
43 {
44     int j;
45 
46     if (i > N)
47     {
48         printf("解法%2d:\n", ++Num);
49         Show();
50     } 
51     else
52     {
53         for (j=1; j<=N; j++)
54         {
55             if (Col[j] && Rtl[i+j] && Ltr[i-j+N])
56             {
57                 Queen[i] = j;//放置皇后
58                 Col[j] = Rtl[i+j] = Ltr[i-j+N] = 0;//位置已经不能放置
59                 Place(i+1);//放置下一个皇后
60                 Col[j] = Rtl[i+j] = Ltr[i-j+N] = 1;//位置重新可用
61             }
62         }
63     }
64 
65     return;
66 }
67 
68 void Show(void)
69 {
70     int i, j;
71 
72     for (i=1; i<=N; i++)
73     {
74         for (j=1; j<=N; j++)
75         {
76             if (Queen[i] == j)
77             {
78                 printf("Q");
79             } 
80             else
81             {
82                 printf(" .");
83             }
84         }
85         printf("\n");
86     }
87     printf("\n");
88 
89     return;
90 }

 

posted @ 2014-09-15 14:10  xxNote  阅读(1021)  评论(0编辑  收藏  举报