八皇后问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
八皇后问题往往还需要使用递归。
Lz查找到几种不同的解决方案,现列出其中两种并做了必要注释,仅供参考。
第一种:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define N 8 4 5 int column[N+1]; // 同栏是否有queen,1表示有 6 int rup[2*N+1]; // 右上至左下是否有queen,1表示有 7 int lup[2*N+1]; // 左上至右下是否有queen,1表示有 8 int queen[N+1] = {0}; 9 int num; //不同解的编号 10 11 void showAnswer() { 12 int x, y; 13 printf("\n解答 %d\n", ++num); 14 15 for(y = 1; y <= N; y++) 16 { 17 for(x = 1; x <= N; x++) 18 { 19 if(queen[y] == x) //queen[y]表示第y行第x个位置有queen 20 { 21 printf(" Q"); 22 } 23 else 24 { 25 printf(" *"); 26 } 27 } 28 printf("\n"); 29 } 30 } 31 32 void backTrack(int i) //递归求解 33 { 34 int j; 35 36 if(i > N) 37 { 38 showAnswer(); //每求出一个解便输出一个 39 } 40 else 41 { 42 for(j = 1; j <= N; j++) 43 { 44 if(column[j] == 1 && rup[i+j] == 1 && lup[i-j+N] == 1) //如果符合条件 45 { 46 queen[i] = j;// 设定[i,j]为占用,第i行第j列为queen 47 48 column[j] = rup[i+j] = lup[i-j+N] = 0; 49 //第i行第j列为queen,所以把第j列设为0;rup[i+j]为0表示经过[i,j]从右上到左下的斜线设为0;lup表示经过[i,j]左上到右下的斜线设为0 50 51 backTrack(i+1); // 到下一行 52 column[j] = rup[i+j] = lup[i-j+N] = 1; //递归操作用到栈,这句会被压倒栈里,最后执行这句,将之前设置成0的位置再设置为1 53 } 54 } 55 } 56 } 57 58 int main(void) { 59 int i; 60 num = 0; 61 62 for(i = 1; i <= N; i++) 63 column[i] = 1; 64 65 for(i = 1; i <= 2*N; i++) //rup和lup下标从 0 到 2*N 66 rup[i] = lup[i] = 1; 67 68 backTrack(1); 69 70 return 0; 71 }
第二种:
1 #include <stdio.h> 2 #include <math.h> 3 4 #define QUEENS 8 5 6 int iCount = 0; //记录解的序号 7 int Site[QUEENS]; //queen在各行上的位置 8 void Queen(int n); //递归求解 9 void Output(); //输出一个解 10 int IsValid(int n); //判断第n个queen放上去之后是否有冲突 11 12 void main() 13 { 14 Queen(0); //从第0行开始递归试探 15 } 16 17 void Queen(int n) 18 { 19 int i; 20 if(n == QUEENS) //参数n从0开始,等于8时便试出了一个解,将它输出并回溯 21 { 22 Output(); 23 return; 24 } 25 26 for(i = 1 ; i <= QUEENS ; i++) //n还没到8,在第n行的各个行上依次试探 27 { 28 Site[n] = i; //在该行的第i行上放置皇后 29 30 if(IsValid(n)) //如果放置没有冲突,就开始下一行的试探 31 Queen(n + 1); 32 } 33 } 34 35 int IsValid(int n) //判断函数 36 { 37 int i; 38 for(i = 0 ; i < n ; i++) //将第n个queen的位置依次于前面n-1个queen的位置比较 39 { 40 if(Site[i] == Site[n]) //判断两个queen是否在同一列 41 return 0; 42 43 if(abs(Site[i] - Site[n]) == (n - i)) //判断两个queen是否在对角线上 44 return 0; 45 } 46 return 1; //没有冲突则返回1 47 } 48 49 void Output() 50 { 51 int i; 52 printf("No.%-5d" , ++iCount); //输出序号,控制格式 53 54 for(i = 0 ; i < QUEENS ; i++) //依次输出各个行上的queen的位置,即所在的列数 55 printf("%d " , Site[i]); 56 57 printf("\n"); 58 }