数据结构课程设计 回溯法解决8皇后n皇后问题
- 格式:doc
- 大小:287.50 KB
- 文档页数:8
数据结构课程设计
学院: 信息科学技术学院
专业: 电子信息工程(1)
姓名: 谢后乐
学号: 20101601310015
N皇后问题
N皇后问题:
在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,
皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于再
n×n的棋盘上放置n个皇后,任何2个皇后不妨在同一行或同一列或同一斜线上。
回溯法简介:
回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。
但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,
这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为
“回溯点”。
我们发现,对于许多问题,所给定的约束集D具有完备性,即i元祖(x1,
x2,…,xi)满足D中仅涉及到x1,x2,…,xi的所有约束意味着j(j<=i)元
组(x1,x2,…)一定也满足D中仅涉及到x1,x2,…,的所有约束,i=1,2,…,
n。换句话说,只要存在0≤j≤n-1,使得(x1,x2,…,)违反D中仅涉及到x1,
x2,…,的约束之一,则以(x1,x2,…,)为前缀的任何n元组(x1,x2,…,
j+1,…,)一定也违反D中仅涉及到x1,x2,…,xi的一个约束,n≥i≥j。因此,
对于约束集D具有完备性的问题P,一旦检测断定某个j元组(x1,x2,…)违
反D中仅涉及x1,x2,…,的一个约束,就可以肯定,以(x1,x2,…)为前
缀的任何n元组(x1,x2,…,)都不会是问题P的解,因而就不必去搜索它们、
检测它们。回溯法正是针对这类问题,利用这类问题的上述性质而提出来的比枚
举法效率更高的算法。
空间树
回溯法首先将问题P的n元组的状态空间E表示成一棵高为n的带权有序
树T,把在E中求问题P的所有解转化为在T中搜索问题P的所有解。树T类
似于检索树,它可以这样构造: 设Si中的元素可排成xi(1) ,xi(2) ,…,
xi(mi-1) ,|Si| =mi,i=1,2,…,n。从根开始,让T的第I层的每一个结点都
有mi个儿子。这mi个儿子到它们的双亲的边,按从左到右的次序,分别带权
xi+1(1) ,xi+1(2) ,…,xi+1(mi) ,i=0,1,2,…,n-1。照这种构造方式,E
中的一个n元组(x1,x2,…)对应于T中的一个叶子节点,T的根到这个叶子
结点的路径上依次的n条边的权分别为x1,x2,…,,反之亦然。另外,对于任
意的0≤i≤n-1,E中n元组(x1,x2,…)的一个前缀I元组(x1,x2,…,xi)
对应于T中的一个非叶子节点,T的根到这个非叶子结点的路径上依次的I条边
的权分别为x1,x2,…,xi,反之亦然。特别,E中的任意一个n元组的空前缀
(),对应于T的根。 因而,在E中寻找问题P的一个解等价于在T中搜
索一个叶子节点,要求从T的根到该叶子结点的路径上依次的n条边相应带的n
个权x1,x2,…满足约束集D的全部约束。在T中搜索所要求的叶子节点,很
自然的一种方式是从根出发,按深度优先的逐步深入,即依次搜索满足约束条件
的前缀1元组(x1i)、前缀2元组(x1,x2)、…,前缀I元组(x1,x2,…,
xi),…,直到i=n为止。 在回溯法中,上述引入的树被称为问题P的状态
空间树;树T上任意一个结点被称为问题P的状态结点;树T上的任意一个叶
子节点被称为问题P的一个解状态结点;树T上满足约束集D的全部约束的任
意一个叶子结点被称为问题P的一个回答状态结点,它对应于问题P的一个解
解题思路:
要解决N皇后问题,其实就是要解决好怎么放置这n个皇后,每一个皇后与
前面的所有皇后不能在同一行、同一列、同一对角线,在这里我们可以以行优先,
就是说皇后的行号按顺序递增,只考虑第i个皇后放置在第i行的哪一列,所以
在放置第i个皇后的时候,可以从第1列判断起,如果可以放置在第1个位置,
则跳到下一行放置下一个皇后。如果不能,则跳到下一列...直到最后一列,如果
最后一列也不能放置,则说明此时放置方法出错,则回到上一个皇后向之前放置
的下一列重新放置。此即是回溯法的精髓所在。当第n个皇后放置成功后,即得
到一个可行解,此时再回到上一个皇后重新放置寻找下一个可行解...如此后,即
可找出一个n皇后问题的所有可行解。
在解决n皇后之前,我们不妨先来看看一个比较简单的例子,也就是8皇后问题,
8皇后也就是n皇后问题中的一种特殊情况,我们只要把8皇后问题解决了,n
皇后问题自然也迎刃而解。根据上面解题思路我们写出8皇后的源程序。
#include
#include
#include
#define a 8
int icount=0;
int x;
int site[100];
void queen(int n);
void out();
int panduan(int n);
void main()
{
queen(0);
}
void queen(int n)
{
int i;
if(n==a)
{
out();
return;
}
for(i=1;i<=a;i++)
{
site[n]=i;
if(panduan(n))
queen(n+1);
}
}
int panduan(int n)
{
int i;
for(i=0;i
if(site[i]==site[n])
return 0;
if(abs(site[i]-site[n])==(n-i))
return 0;
}
return 1;
}
void out()
{
int i;
printf("no.%-5d",++icount);
for(i=0;i{
printf("%d",site[i]);
}
printf("/n");
}
以上就是8皇后的源程序,可以得知,n皇后问题就是8皇后问题的扩展,我
们只需把程序中8*8棋盘变成n*n棋盘就行了。
现在画出程序的框架图。
是
是 i++
否
上面就是整个程序的原理框图,下面我们来写出整个程序。
#include
设置n值的数据
放置第i个皇后,判断i是否
等于n值
放置第i个皇后(寻找一个位子)
输出路径与总解决方
法加1
判断是否符合要求
#include
#include
#define a x
int icount=0;
int x;
int site[100];
void queen(int n);
void out();
int panduan(int n);
void main()
{
printf("shuruyigeno:");
scanf("%d",&x);
queen(0);
}
void queen(int n)
{
int i;
if(n==a)
{
out();
return;
}
for(i=1;i<=a;i++)
{
site[n]=i;
if(panduan(n))
queen(n+1);
}
}
int panduan(int n)
{
int i;
for(i=0;i
if(site[i]==site[n])
return 0;
if(abs(site[i]-site[n])==(n-i))
return 0;
}
return 1;
}
void out()
{
int i;
printf("no.%-5d",++icount);
for(i=0;i{
printf("%d",site[i]);
}
printf("/n");
}
心得与体会:
解决这个问题,我们还是得从一些比较直观的问题入手,其实n皇后最难
的一点是你觉得这个n值是一个变量,是一个不确定的数,所以会感到比较抽象,
但是只要你把这个n值想成8,转换为我们熟知的8皇后问题,也就解决了问题
的一大半,整个程序的思路和八皇后没有太大的区别,
运用回溯法,虽然是解决了n皇后的问题,不过感觉程序运行效率并不高,
每次放置皇后,都要做很多判断,所以程序的复杂度相当繁琐。