回溯法解决八皇后问题
说明西洋棋中的皇后可以直线前进,吃掉遇到的所有棋子,如果棋盘上有八个皇后,则这八个皇后如何相安无事的放置在棋盘上,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 }