问题描述和要求
按照国际象棋的规则,一个皇后可以攻击与之处在同一行或是同一列或同一条斜线上的其他任何棋子。则n皇后问题的要求为,在一个n×n的棋盘上放置n个皇后,使得任何两个皇后不能被放在同一行或同一列或同一条斜线上。
回溯法原理
回溯法可以系统地搜索一个问题的所有解或任一解。它在包含所有问题的解空间树中,按照深度优先策略,从根节点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先回溯;否则,进入该子树,继续按深度优先的策略进行搜索。
回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束;而用来求问题的任一解时,只要搜索到问题的一个解就可以结束。
n皇后问题求解过程
从空棋盘开始,设在第1行至第m行都已经正确地放置了m个皇后,再在第m+1行上找合适的位置放置第m+1个皇后,直到在第n行找到了合适的位置放置第n个皇后,此时就找到了一个解。改变第n行皇后的位置,希望获得下一个解。
【通过回溯法获得问题的解,所以不是按照正常的思维从第一个皇后开始重新摆放找第二个解】
代码实现
/**
判断当先位置是否可以放置皇后,即不与已经放置的皇后发生冲突
*/
int Place(int* Column,int index)
{
int i;
for(i=1;i<index;i++)
{
int Column_differ=abs(Column[index]-Column[i]); //得到之前已放置的皇后和将要放置的皇后的列数之差
int Row_differ=abs(index-i); //得到之前已放置的皇后和将要放置的皇后的行数之差
/*有皇后与将要放置的皇后在同一列或同一斜线上,则返回0,表示无法放置*/
if(Column[i]==Column[index]||Column_differ==Row_differ)
{
return 0;
}
}
return 1; //没有皇后与将要放置的皇后在同一列或同一斜线上,则返回1,表示可以放置
}
void N_Queue(int n)
{
int Column_Num[n+1]; //数组元素为某个皇后所在的列
int index=1; //皇后的索引,从1到n
int i;
int answer_num=0; //n皇后问题解的个数
/*初始化数组*/
for(i=1;i<=n;i++)
{
Column_Num[i]=0;
}
while(index>0)
{
Column_Num[index]++;//放置皇后
/*如果准备放置的地方不合适,则要将当前皇后移动到下一个位置,直到找到合适的位置*/
while(Column_Num[index]<=n&&!Place(Column_Num,index))
{
Column_Num[index]++;
}
if(Column_Num[index]<=n)
{
if(index==n) //当最后一个皇后放置成功
{
answer_num++; //此问题的解+1
/*将最后一个皇后移除棋盘格
使得在下一次循环中不满足Column_Num[index]<=n这个条件,以求index--,即回溯到上一个皇后
去寻找下一个解*/
for(i=1;i<=n;i++)
{
Column_Num[index]++;
}
}
else //如果还没放置到最后一个皇后,则继续寻找下一个皇后的位置
{
index++;
Column_Num[index]=0;
}
}
else
{
index--; //当前皇后无法放置,则回溯到上一个皇后
}
}
}
备注:
1.本篇的回溯法原理和代码参考于《软件设计师教程》
2.本代码未对解的个数或是解的方案进行输出,可自行加入