算法学习->递归典例N皇后问题
00 问题
在N✖N(这个N==N皇后的N)的方格棋盘上放置n个皇后,要求:1.每个皇后在不同行不同列;2.每个皇后在不同左右对角线
输出要求:输出符合条件的所有解,解以皇后的坐标的形式。
01 思路
拿到这个问题如果用蛮力,那么OK,把皇后序号与行的序号关联在一起,去探查列,每多放一个皇后,就是一遍遍地回头遍历检查二维数组的事情;
但是如果想一下,用归纳法,我们考虑一下每一次放皇后的过程,我们需要考虑哪些因素,以及执行哪些步骤?
如果是放第一个皇后,那么ok,无条件返回true,因为没有限制条件,第一行任何一列都符合条件。
else 如果是中间的皇后,比如第i个皇后,那么从第i行头考虑,逐列探查,每一列得考虑前i-1的皇后,每一个都得拎出来检查一下条件,来确保第i个皇后所放的列是对的
这样就会发现,每一次放皇后取决于前面的皇后,而第一个随便放,皇后个数不超过n,这样就可以设计递归出口和递归主体了。出口是到n,主体就是前面的if-else。
主体的if-else可以再分几步:
-
逐列,是一次循环
-
-
探查函数中,探查需从第0个皇后查起,是一个循环
-
探查函数返回许可,主控函数queen递归下一个皇后
-
递归终点即n行走完,所有皇后落位。
02 代码
1 #include<stdio.h> 2 #include<stdlib.h> 3 #define N 20 4 5 int q[N];//存皇后的列数(从0开始 6 int count = 0; 7 void dispasolution(int n){ 8 printf("The %dth个解:", ++count); 9 for(int i=0;i<=n;i++){ 10 printf("(%d,%d)", i,q[i]); 11 } 12 printf("\n"); 13 } 14 bool place(int i, int j){ 15 if(i==1)return true;//第一个必定正确 16 int k=1; 17 while(k<i){ 18 if(q[k]==j||abs(q[k]-j)==abs(i-k)){ 19 return false; 20 } 21 k++; 22 } 23 return true; 24 } 25 void queen(int i, int n){ 26 if(i>n)dispasolution(n);//递归出口,输出 27 else{ 28 for(int j=1;j<=n;j++){ 29 if(place(i,j)){//如果符合条件就把第i个皇后放下去,进一步递归 30 q[i]=j; 31 queen(i+1,n); 32 } 33 } 34 } 35 } 36 37 int main(){ 38 int n; 39 printf("皇后问题(n<20)n="); 40 scanf("%d",&n); 41 if(n>20){ 42 printf("n is too big!\n"); 43 } 44 else{ 45 printf("%d皇后问题的求解如下:\n",n); 46 queen(1,n);//调用递归主控函数 47 } 48 return 0; 49 }
03 测试
示例 n=6
1 皇后问题(n<20)n=6 2 6皇后问题的求解如下: 3 The 1th个解:(0,0)(1,2)(2,4)(3,6)(4,1)(5,3)(6,5) 4 The 2th个解:(0,0)(1,3)(2,6)(3,2)(4,5)(5,1)(6,4) 5 The 3th个解:(0,0)(1,4)(2,1)(3,5)(4,2)(5,6)(6,3) 6 The 4th个解:(0,0)(1,5)(2,3)(3,1)(4,6)(5,4)(6,2)