一.实验目的
1. 了解皇后相互攻击的条件:如果任意两个皇后在同一行,同一列或同一对角线,则她们相互攻击。
2. 运用迭代的方法实现n皇后问题,求解得到皇后不相互攻击的一个解
二.实验内容
基本思路:用n元组x[1:n]表示n后问题的解,其中x[i]表示第i个皇后放在棋盘的第i行的第x[i]列。
抽象约束条件得到能放置一个皇后的约束条件:(1)x[i]!=x[k]; (2)abs(x[i]-x[k])!=abs(i-k)。
应用回溯法,当可以放置皇后时就继续到下一行,不行的话就返回到第一行,重新检验要放的列数,如此反复,直到将所有解解出。
在回溯法中,递归函数Backtrack(1)实现对整个解空间的回溯搜索。
Backtrack(i)搜索解空间的第i层子树。
类Queen的数据成员记录解空间的节点信息,以减少传给Backtrack函数的参数。
sum记录当前已找到的可行方案数。
运用回溯法解题通常包含以下三个步骤:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
源代码:
#include<iostream>
using namespace std;
class Queen{
friend int nQueen(int);
private:
bool Place(int k);
void Backtract(int t);
int n,*x;
long sum; //可行方案数
};
bool Queen::Place(int k)
{
for(int j=1;j<k;j++)
if((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) return false;
return true;
}
void Queen::Backtract(int t)
{
if (t>n)
{
sum++;
cout<<"第"<<sum<<"种方法:";
for(int i=1;i<=n;i++)
cout<<x[i]<<" ";
cout<<endl;
}
else{
for(int i=1;i<=n;i++){
x[t]=i;
if(Place(t)) Backtract(t+1);
}
}
}
int nQueen(int n)
{
Queen X;
X.n=n;
X.sum=0;
int *p=new int[n+1];
for(int i=0;i<=n;i++)
p[i]=0;
X.x=p;
X.Backtract(1);
delete []p;
return X.sum;
}
void main()
{
int n,m;
cout<<"请输入皇后个数:";
cin>>n;
m=nQueen(n);
cout<<endl;
cout<<"有"<<m<<"种可行方法"<<endl; }
三.实验的结果及分析
……………
四.实验心得体会
通过这次实验理解了回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。
这个开始结点就成为一个活结点,同时也成为当前的扩展结点。
在当前的扩展结点处,搜索向纵深方向移至一个新结点。
这个新结点就成为一个新的活结点,并成为当前扩展结点。
如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。
换句话说,这个结点不再是一个活结点。
此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。
回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。
培养了我独立编程和调试程序的能力,使我对算法的分析与设计有更深刻的认识。