回溯法解决八皇问题

简介: 把八个皇后放在一个8*8的棋盘上面,要求同一行、同一列、同一对角线不能有两个皇后。思路:关键在于判定两个皇后是否在同一行、同一列或同一对角线上。这里,棋盘下标从1开始算起。观察发现:若是在同一行,则行号相同;若在同一列,则列号相同;若在同一“/”对角线,则行列值之和相同;若是在同一“\”对角线,则行列值之差相同。

把八个皇后放在一个8*8的棋盘上面,要求同一行、同一列、同一对角线不能有两个皇后。

思路:
关键在于判定两个皇后是否在同一行、同一列或同一对角线上。这里,棋盘下标从1开始算起。
观察发现:
若是在同一行,则行号相同;若在同一列,则列号相同;
若在同一“/”对角线,则行列值之和相同;若是在同一“\”对角线,则行列值之差相同。
考虑到每行仅有一个皇后,设一维数组a[1...8]表示皇后的位置:第i行第j列放置皇后,则a[i]=j,即下标是行数,值是列数。
判断皇后是否安全,即检查同一列、同一对角线是否已经有皇后。
建立标志数组b[1...8]能控制同一列只能有一个皇后。

若两个皇后在同一对角线上,则其行列值之和或差相等,故亦可以建立标志数组c[1...16],d[-7..7]控制同一对角线上只能有一个皇后。(注意:这里的c和d数组是对应主、副各自16条对角线的.)
若是斜线不分方向,则同一斜线上两个皇后的行号之差的绝对值与列号之差的绝对值相等。

在这种方式下,要表示两个皇后i和j不在同一列或斜线上的条件可以描述为:(a[i]!=a[j])&&(abs(a[i]-a[j]))
i和j分别表示两个皇后的行号。

具体代码如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int n;
 4 int sum=0,a[100];//sum表示总的方案数。a[i]=x表示第i行放在x列
 5 int b[100]={0},c[100]={0},d[100]={0};
 6 //b[i]=1表示第i列有皇后。
 7 //c[i]=1表示按从左上到右下顺序的第i条副对角线(“/”对角线)上有皇后
 8 //d[i]=1表示按从右上到左下顺序的第i条主对角线(“\”对角线)上有皇后
 9 int search(int i);//递归回溯放置第i个皇后(放在第i行)
10 void print();      //输出方案
11 int main()
12 {
13     n=8;//皇后问题的阶数(皇后的个数)
14     search(1);
15     if(sum==0) printf("no solution!\n");
16     return 0;
17 }
18 int search(int i)//递归回溯放置第i个皇后(放在第i行)
19 {
20     int j;
21     for(j=1;j<=n;j++)
22     {
23         if((!b[j])&&(!c[i+j])&&(!d[i-j+7]))
24         {
25             a[i]=j;
26             b[j]=1; c[i+j]=1; d[i-j+7]=1;
27             if(i==n) print(); //n个皇后已经放置完成,打印放置的方案
28             else search(i+1); //继续递归放置下一个皇后
29             b[j]=0; c[i+j]=0; d[i-j+7]=0;  //恢复现场,当前位置的皇后清除
30         }
31     }
32 }
33 void print()
34 {
35     int i;
36     sum++;
37     printf("sum=%d\n",sum);
38     for(i=1;i<=n;i++)
39         printf("%-4d ",a[i]);
40     printf("\n");
41 }

PS:个人觉得该段代码里面标志数组b、c、d用的非常巧妙。

上面的代码是多用了两个数组c、d简化了判断。假如只用一个数组b也能够判断皇后会否冲突,只是时间效率降低了很多。代码如下:

 1 #include <stdio.h>
 2 int n;
 3 int sum=0,a[100]; //sum表示总的方案数。a[i]=x表示第i行放在x列
 4 int b[100]={0};   //b[i]=1表示第i列有皇后。
 5 
 6 int search(int i);//递归回溯放置第i个皇后(放在第i行)
 7 void print();      //输出方案
 8 int main()
 9 {
10     n=8;//皇后问题的阶数(皇后的个数)
11     search(1);
12     if(sum==0) printf("no solution!\n");
13     return 0;
14 }
15 int search(int i)//递归回溯放置第i个皇后(放在第i行)
16 {
17     int j,k;
18     for(j=1;j<=n;j++)
19     {
20         if(b[j]==0)
21         {
22             for(k=1;k<i;k++)//扫描a[]前i-1项,检查前i-1个皇后会否与第i个皇后冲突 
23             {
24                 if(k+a[k]==i+j) break;
25                 else if(k-a[k]==i-j) break;
26             }
27             if(k==i)//前i-1个皇后不与第i个皇后冲突
28             {
29                 a[i]=j;
30                 b[j]=1;
31                 if(i==n) print(); //n个皇后已经放置完成,打印放置的方案
32                 else search(i+1); //继续递归放置下一个皇后
33                 b[j]=0; //恢复现场,当前位置的皇后清除
34             }
35         }
36     }
37 }
38 void print()
39 {
40     int i;
41     sum++;
42     printf("sum=%d\n",sum);
43     for(i=1;i<=n;i++)
44         printf("%-4d ",a[i]);
45     printf("\n");
46 }
View Code

 输出二维数组结果:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 int n;
 5 int sum=0,a[100];//sum表示总的方案数。a[i]=x表示第i行放在x列
 6 int b[100]={0},c[100]={0},d[100]={0};
 7 //b[i]=1表示第i列有皇后。
 8 //c[i]=1表示按从左上到右下顺序的第i条副对角线(“/”对角线)上有皇后
 9 //d[i]=1表示按从右上到左下顺序的第i条主对角线(“\”对角线)上有皇后
10 
11 int search(int i);//递归回溯放置第i个皇后(放在第i行)
12 void print();      //输出方案
13 int main()
14 {
15     n=8;//皇后问题的阶数(皇后的个数)
16     search(1);
17     if(sum==0) printf("no solution!\n");
18     return 0;
19 }
20 int search(int i)//递归回溯放置第i个皇后(放在第i行)
21 {
22     int j;
23     for(j=1;j<=n;j++)
24     {
25         if((!b[j])&&(!c[i+j])&&(!d[i-j+7]))
26         {
27             a[i]=j;
28             b[j]=1; c[i+j]=1; d[i-j+7]=1;
29             if(i==n) print(); //n个皇后已经放置完成,打印放置的方案
30             else search(i+1); //继续递归放置下一个皇后
31             b[j]=0; c[i+j]=0; d[i-j+7]=0;  //恢复现场,当前位置的皇后清除
32         }
33     }
34 }
35 void print()
36 {
37     int i,j;
38     int t[101][101]={0};
39     sum++;
40     printf("No. %d\n",sum);
41     for(i=1;i<=n;i++)
42         printf("%-4d ",a[i]);
43         //t[i][a[i]]=1;
44     /*for(i=1;i<=n;i++)
45     {
46         for(j=1;j<=n;j++)
47             printf("%d ",t[i][j]);
48            printf("\n");
49     }*/
50     printf("\n");
51 }
View Code

 

相关文章
|
算法 索引
回溯算法
回溯算法
64 0
|
6月前
|
机器学习/深度学习 人工智能 算法
回溯算法是怎样的
回溯算法,择优搜索:树的深搜+剪枝
|
7月前
|
算法 C++
回溯算法详解
回溯算法详解
106 0
|
算法
这一次,真正理解回溯算法
回溯算法思想很简单,大部分都是用来解决广义搜索问题:从一组可能解中,选出一个满足要求的解。 回溯非常适合用递归实现,剪枝是提高回溯效率的一种技巧,无需穷举搜索所有情况。 回溯算法可解决很多问题,如DFS、八皇后、0-1背包、图的着色、旅行商、数独、全排列、正则表达式匹配等。
185 0
这一次,真正理解回溯算法
|
机器学习/深度学习 算法 网络协议
回溯法
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。 回溯是递归的副产品,只要有递归就会有回溯。 所以以下讲解中,回溯函数也就是递归函数,指的都是一个函数。 回溯法的效率 回溯法的性能如何呢,这里要和大家说清楚了,虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法。 因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。 那么既然回溯法并不高效为什么还要用它呢? 因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。 此时大家应该好奇了,都什么问题,这么牛逼,只能暴力搜索。 回溯法
|
人工智能 算法
【算法】回溯法的应用
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
255 0
【回溯法】子集合问题
【回溯法】子集合问题
163 0
【回溯法】子集合问题