回溯算法与八皇后问题N皇后问题Word版
算法——八皇后问题(eightqueenpuzzle)之回溯法求解
算法——⼋皇后问题(eightqueenpuzzle)之回溯法求解⼋皇后谜题是经典的⼀个问题,其解法⼀共有92种!其定义:1. ⾸先定义⼀个8*8的棋盘2. 我们有⼋个皇后在⼿⾥,⽬的是把⼋个都放在棋盘中3. 位于皇后的⽔平和垂直⽅向的棋格不能有其他皇后4. 位于皇后的斜对⾓线上的棋格不能有其他皇后5. 解出能将⼋个皇后都放在棋盘中的摆法这个问题通常使⽤两种⽅法来求解:1. 穷举法2. 回溯法(递归)本⽂章通过回溯法来求解,回溯法对⽐穷举法⾼效许多,让我们学习如何实现吧!实现思想:1. 我们先在棋盘的第0⾏第1个棋格放下第⼀个皇后2. 下⼀⾏寻找⼀个不冲突的棋格放下下⼀个皇后3. 循环第2步4. 如果到某⼀⾏全部8个格⼦都⽆法放下皇后,回溯到前⼀⾏,继续寻找下⼀个不冲突的棋格5. 把8个皇后都放在棋盘之后,输出或存储摆法,结束实现(Java)算法:定义棋盘我们通过⼀个⼆维整型数组表⽰⼀个棋盘数组内为1是放下了的皇后,0则是空⽩的棋格我们下下⾯定义⼀个⽅法:通过检查棋格是否为1来知道是不是有皇后1// 定义⼀个棋盘2static int chessboard[][] = new int[8][8];检查冲突这个⽅法⽤来检查冲突:在⽔平垂直⽅向、斜⾓上的棋格有⽆其他皇后,传⼊的(x,y)是需要检查的棋格,如检查棋格(1,0)即棋盘的第2⾏第1个,是否能放下皇后。
1// 检查是否符合规则2private static boolean checked(int x,int y){3for(int i = 0;i<y;i++){4// 检查⽔平垂直⽅向5if(chessboard[x][i]==1)return false;6// 检测左斜⾓7if((x-y+i>=0)&&chessboard[x-y+i][i]==1)return false;8// 检查右斜⾓9if((x+y-i<=7)&&chessboard[x+y-i][i]==1)return false;10 }11return true;12 }放下皇后我们在每⼀⾏都执⾏以下步骤,通过从第1个棋格到第8个遍历寻找可以放下皇后的棋格如果放下了皇后,我们就可以继续放下下⼀个了,将⾏数+1,我们递归调⽤这个⽅法1public static boolean solve(int y){2// 将⼀⾏的8种情况都扫描⼀次3for(int i = 0;i<8;i++){4// 每次检测前都将当前⾏清空,避免脏数据5for(int k = 0;k<8;k++)chessboard[k][y]=0;6if(checked(i, y)){7 chessboard[i][y] = 1;8// 当前⼀⾏已经获得解法,进⼊下⼀⾏9 solve(y+1);10 }11 }12return false;13 }算法边界当我们放下了所有8个皇后后,需要⼀个终⽌条件,我们在⾏数y=8时,结束算法同时你可以输出⼀个棋盘摆法了!恭喜你已经把这个经典问题解决了!1// 当y=8时,已经找到⼀种解决⽅法2if(y == 8){3return true;4 }以下是完整的算法1public class EightQueen{2// 定义⼀个棋盘3static int chessboard[][] = new int[8][8];4// 计数器5static int count = 0;67// 解题⽅法8public static boolean solve(int y){9// 当y=8时,已经找到⼀种解决⽅法,计数器加⼀并输⼊摆法10if(y == 8){11 System.out.println("solved!");12 show();13 count++;14return true;15 }16// 将⼀⾏的8种情况都扫描⼀次17for(int i = 0;i<8;i++){18// 每次检测前都将当前⾏清空,避免脏数据19for(int k = 0;k<8;k++)chessboard[k][y]=0;20if(checked(i, y)){21 chessboard[i][y] = 1;22// 当前⼀⾏已经获得解法,进⼊下⼀⾏23 solve(y+1);24 }25 }26return false;27 }28// 检查是否符合规则29private static boolean checked(int x,int y){30for(int i = 0;i<y;i++){31// 检查垂直⽅向32if(chessboard[x][i]==1)return false;33// 检测左斜⾓34if((x-y+i>=0)&&chessboard[x-y+i][i]==1)return false;35// 检查右斜⾓36if((x+y-i<=7)&&chessboard[x+y-i][i]==1)return false;37 }38return true;39 }40// 输出棋盘摆法41public static void show(){42for(int i = 0;i<8;i++){43for(int j = 0;j<8;j++){44 System.out.print(chessboard[j][i]+" ");45 }46 System.out.println("");47 }48 }49 }在执⾏这个算法后:have 92 ways to sovle it!我们获得了92种棋盘摆法!。
八皇后问题(经典算法-回溯法)
⼋皇后问题(经典算法-回溯法)问题描述:⼋皇后问题(eight queens problem)是⼗九世纪著名的数学家⾼斯于1850年提出的。
问题是:在8×8的棋盘上摆放⼋个皇后,使其不能互相攻击。
即任意两个皇后都不能处于同⼀⾏、同⼀列或同⼀斜线上。
可以把⼋皇后问题扩展到n皇后问题,即在n×n的棋盘上摆放n个皇后,使任意两个皇后都不能互相攻击。
思路:使⽤回溯法依次假设皇后的位置,当第⼀个皇后确定后,寻找下⼀⾏的皇后位置,当满⾜左上、右上和正上⽅向⽆皇后,即矩阵中对应位置都为0,则可以确定皇后位置,依次判断下⼀⾏的皇后位置。
当到达第8⾏时,说明⼋个皇后安置完毕。
代码如下:#include<iostream>using namespace std;#define N 8int a[N][N];int count=0;//判断是否可放bool search(int r,int c){int i,j;//左上+正上for(i=r,j=c; i>=0 && j>=0; i--,j--){if(a[i][j] || a[i][c]){return false;}}//右上for(i=r,j=c; i>=0 && j<N; i--,j++){if(a[i][j]){return false;}}return true;}//输出void print(){for(int i=0;i<N;i++){for(int j=0;j<N;j++){cout<<a[i][j]<<" ";}cout<<endl;}}//回溯法查找适合的放法void queen(int r){if(r == 8){count++;cout<<"第"<<count<<"种放法\n";print();cout<<endl;return;}int i;for(i=0; i<N; i++){if(search(r,i)){a[r][i] = 1;queen(r+1);a[r][i] = 0;}}}//⼊⼝int main(){queen(0);cout<<"⼀共有"<<count<<"放法\n"; return 0;}。
回溯法解八皇后问题
fprintf(fp," ");
}
printf("%d\n", i);
fprintf(fp, "%d\n", i);
}
printf
(".......................................................\n");
fprintf(fp,
/*/
#include <stdio.h>
#include <math.h>
#define false 0
#define true 1
#define quesize 8
int gx[quesize+1];
int sum=0;
int place( int k );
void print( int a[] );
fprintf(fp,"the sum of the ways of queens:%d\n", sum);
printf("the sum of the ways of queens:%d\n", sum);
fclose(fp);
return 1;
}
/*/////////////////////////////////////////////////////////////////////
{
int i = 1;
while ( i < k )
{ if ( ( gx[i] == gx[k] ) || ( abs( gx[i] - gx[k] )==abs( i - k ) )
数据结构与算法中的“递归”——用回溯法求解8皇后问题
八皇后问题是一个古老而著名的问题,它是回溯算法的典型例题。
该问题是十九世纪德国著名数学家高斯于1850年提出的:在8行8列的国际象棋棋盘上摆放着八个皇后。
若两个皇后位于同一行、同一列或同一对角线上,则称为它们为互相攻击。
在国际象棋中皇后是最强大的棋子,因为它的攻击范围最大,图6-15显示了一个皇后的攻击范围。
图6-15 皇后的攻击范围现在要求使这八个皇后不能相互攻击,即任意两个皇后都不能处于同一行、同一列或同一对角线上,问有多少种摆法。
高斯认为有76种方案。
1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
现代教学中,把八皇后问题当成一个经典递归算法例题。
图6-16显示了两种八个皇后不相互攻击的情况。
图6-16八个皇后不相互攻击的情况现在来看如何使用回溯法解决八皇后问题。
这个算法将在棋盘上一列一列地摆放皇后直到八个皇后在不相互攻击的情况下都被摆放在棋盘上,算法便终止。
当一个新加入的皇后因为与已经存在的皇后之间相互攻击而不能被摆在棋盘上时,算法便发生回溯。
一旦发生这种情况,就试图把最后放在棋盘上的皇后移动到其他地方。
这样做是为了让新加入的皇后能够在不与其它皇后相互攻击的情况下被摆放在棋盘的适当位置上。
例如图6-17所示的情况,尽管第7个皇后不会与已经放在棋盘上的任何一皇后放生攻击,但仍然需要将它移除并发生回溯,因为无法为第8个皇后在棋盘上找到合适的位置。
图6-17 需要发生回溯的情况算法的回溯部分将尝试移动第7个皇后到第7列的另外一点来为第8个皇后在第8列寻找一个合适的位置。
如果第7个皇后由于在第7列找不到合适的位置而无法被移动,那么算法就必须去掉它然后回溯到第6列的皇后。
最终算法不断重复着摆放皇后和回溯的过程直到找到问题的解为止。
下面给出了求解八皇后问题的示例程序。
#include <conio.h>#include <iostream>using namespace std;// 首先要求皇后不冲突,那么每行只应该有一个皇后// 用queens[]数组在存储每个皇后的位置// 例如: queens[m] = n 表示第m行的皇后放在第n列上#define MAX 8int sum = 0;class QueenPuzzle{int queens[MAX]; // 存储每行皇后的列标public:void printOut(); // 打印结果int IsValid(int n); //判断第n个皇后放上去之后,是否合法 void placeQueen(int i); // 递归算法放置皇后};void QueenPuzzle::printOut(){for(int i=0; i<MAX; i++){for(int j=0; j<MAX; j++){if(j == queens[i])cout << "Q ";elsecout << "0 ";}cout << endl;}cout << endl << "按q键盘退出,按其他键继续" << endl << endl;if(getch() == 'q')exit(0);}// 在第i行放置皇后void QueenPuzzle::placeQueen(int i){for(int j=0; j<MAX; j++){// 如果全部放完了输出结果if(i == MAX){sum ++;cout << "第" << sum << "组解:" << endl;printOut();return;}// 放置皇后queens[i] = j;// 此位置不能放皇后继续试验下一位置if(IsValid(i))placeQueen(i+1);}}//判断第n个皇后放上去之后,是否合法,即是否无冲突int QueenPuzzle::IsValid(int n){//将第n个皇后的位置依次于前面n-1个皇后的位置比较。
回溯法求解N皇后问题
算法的实现
• 假设回溯法要找出所有的答案结点 。 • 设(x1,x2,…,xi-1)是状态空间树中由根到一个结 点的路径,而T(x1,…xi-1)是下述所有结点xi的 集合,它使得对于每一个xi,(x1,x2,…,xi)是由 根到一个结点xi的路径;假定还存在着一些限 界函数Bi,如果路径(x1,x2,…,xi)不可能延伸到 一个答案结点,则Bi(x1,x2,…,xi)取假值,否则 取真值。 • 于是解向量X(1:n)中的第i个分量,就是那些 选自集合T (x1,x2,…,xi-1)且使Bi为真的xi
HHIT
算法8.5:n-皇后问题的解
Algorithm
Procedure NQUEENS(n) //此过程使用回溯法求出一个n*n棋盘上放置n个皇后,使其不能互相攻 击的所有可能位置// integer k,n,X(1:n) X(1)0 ; k1 // k是当前行;X(k)是当前列 // while k>0 do // 对所有的行,执行以下语句 // X(k)X(k)+1 //移到下一列// while X(k)<=n and Not PLACE(k) do //此处能放这个皇后吗// X(k)X(k)+1 //不能放则转到下一列// repeat if X(k)<=n then //找到一个位置// if k=n then print (X) //是一个完整的解则打印这个数组// else kk+1;X(k)0 //否则转到下一行// end if else kk-1 //回溯// end if repeat End NQUEENS
HHIT
Algorithm
显然,棋盘的每一行上可以而且必须摆放一个皇后, 所以,n皇后问题的可能解用一个n元向量X=(x1, x2, …, xn) 表示,其中,1≤i≤n并且1≤xi≤n,即第i个皇后放在第i行第 xi列上。 由于两个皇后不能位于同一列上,所以,解向量X必 须满足约束条件: xi≠xj (式8.1)
八皇后问题详细的解法
若无法放下皇后则回到上一行, 即回溯
当n行的皇后都已确定后,我们 就找到了一种方案
17
check2 (int a[ ],int n)
queen21(例) 1 b加约束的枚举算法{//i多nt次i; 被调用,只是一重循环
{int a[9];
for(i=1;i<=n-1;i++)
for (a[1]=1;a[1]<=8;a[1]++)
{ if (i=n) 搜索到一个解,输出; //搜索到叶结点
else
//正在处理第i个元素
{a[i]第一个可能的值;
while (a[i]不满足约束条件且在搜索空间内)
a[i]下一个可能的值;
if (a[i]在搜索空间内)
{标识占用的资源; i=i+1;} //扩展下一个结点
else {清理所占的状态空间;i=i-1;} //回溯
按什么顺序去搜? 目标是没有漏网之鱼,尽量速度快。
5
2 【问题设计】盲目的枚举算法
a 盲目的枚举算法
通过8重循环模拟搜索空间中的88个状态;
按枚举思想,以DFS的方式,从第1个皇后在第1列开 始搜索,枚举出所有的“解状态”:
从中找出满足约束条件的“答案状态”。
约束条件?
题,而不能解决任意
}}}}}}}
的n皇后问题。
18
2 回溯法应用-算法说明
八皇后问题中的核心代码: 遍历过程函数; check函数。
解决此类问题的核心内容: 解空间树的搜索算法; 估值/判断函数:判断哪些状态适合继续扩展,或者作 为答案状态。
19
2 回溯法应用-n皇后问题
for(j=1;j<=i-1;j++)
八皇后以及N皇后问题分析
⼋皇后以及N皇后问题分析⼋皇后是⼀个经典问题,在8*8的棋盘上放置8个皇后,每⼀⾏不能互相攻击。
因此拓展出 N皇后问题。
下⾯慢慢了解解决这些问题的⽅法:回溯法:回溯算法也叫试探法,它是⼀种系统地搜索问题的解的⽅法。
回溯算法的基本思想是:从⼀条路往前⾛,能进则进,不能进则退回来,换⼀条路再试。
在现实中,有很多问题往往需要我们把其所有可能穷举出来,然后从中找出满⾜某种要求的可能或最优的情况,从⽽得到整个问题的解。
回溯算法就是解决这种问题的“通⽤算法”,有“万能算法”之称。
N皇后问题在N增⼤时就是这样⼀个解空间很⼤的问题,所以⽐较适合⽤这种⽅法求解。
这也是N皇后问题的传统解法,很经典。
算法描述:1. 算法开始,清空棋盘。
当前⾏设为第⼀⾏,当前列设为第⼀列。
2. 在当前⾏,当前列的判断放置皇后是否安全,若不安全,则跳到第四步。
3. 在当前位置上满⾜条件的情况: 在当前位置放⼀个皇后,若当前⾏是最后⼀⾏,记录⼀个解; 若当前⾏不是最后⼀⾏,当前⾏设为下⼀⾏,当前列设为当前⾏的第⼀个待测位置; 若当前⾏是最后⼀⾏,当前列不是最后⼀列,当前列设为下⼀列; 若当前⾏是最后⼀⾏,当前列是最后⼀列,回溯,即清空当前⾏以及以下各⾏的棋盘,然后当前⾏设为上⼀⾏,当前列设为当前⾏的下⼀个待测位置; 以上返回第⼆步。
4.在当前位置上不满⾜条件: 若当前列不是最后⼀列,当前列设为下⼀列,返回到第⼆步; 若当前列是最后⼀列,回溯,即,若当前⾏已经是第⼀⾏了,算法退出,否则,清空当前⾏以及以下各⾏的棋盘,然后,当前⾏设为上⼀⾏,当前列设为当前⾏的下⼀个待测位置,返回第⼆步。
如何判断是否安全:把棋盘存储为⼀个N维数组a[N],数组中第i个元素的值代表第i⾏的皇后位置,这样便可以把问题的空间规模压缩为⼀维O(N),在判断是否冲突时也很简单, ⾸先每⾏只有⼀个皇后,且在数组中只占据⼀个元素的位置,⾏冲突就不存在了, 其次是列冲突,判断⼀下是否有a[i]与当前要放置皇后的列j相等即可。
数据结构课程设计 回溯法解决8皇后n皇后问题
数据结构课程设计学院:信息科学技术学院专业:电子信息工程(1)姓名:谢后乐学号:20101601310015N皇后问题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。
八皇后问题的最佳解决方案
② 算法描述
3.2 递归回溯法解决八皇后问题
int a[20],b[20],c[40],d[40]; int n,t,i,j,k; //t记录解的个数,i掌握行,j掌握列
main( )
{ int i, input(n); //输入皇后的个数
for(i=1;i<=n;i++) { b[i]=0;//记录棋盘n个列 c[i+1]=0; c[n+i]=0;//记录棋盘负对角线 d[i]=0; d[n+i-1]=0;//记录棋盘主对角线
2 算法设计与分析
报告3 算法设计与分析试验报告
八皇后问题的最正确解决方 案
内容提要
1 回溯法概述 2 八皇后问题 3 解决八皇后问题常用算法 4 算法分析与总结
1 回溯法概述
一 回溯法
回溯法实际是一个类似枚举的搜寻尝试方 法,它的主题思想是在搜寻尝试中找问题的 解,当不满足求解条件就”回溯”(返回),尝 试别的路径。回溯算法是尝试搜寻算法中最 为根本的一种算法,其承受了一种“走不通就 掉头”的思想,作为其掌握构造。本文主要 描述递归回溯与非递归回溯,并用这两个算 法解决经典的“八皇后”问题,找出该问题的 最正确解决方案。
3.2 非递归回溯法解决八皇后问题
t a[20],n;
Main2
{ input(n); bckdate〔n〕;} //初始化,输入皇后数目
backdate (int n) //该函数是用来查找满足约束的全部解
{ int k;
a[1]=0; k=1; //k用来表示第k个皇后
while( k>0 ) {a[k]=a[k]+1; while ((a[k]<=n) and (check(k)=0)) //搜
皇后问题详细的解法
for(a[7]=1;a[7]<=8;a[7]++} )
for(a[8]=1;a[8]<=8;a[8]++){
if (check(a,8)=0) continue;
else
for(i=1;i<=8;i++)print(a[i]);
}
10
}
1 回溯法
有“通用的解题法”之称。 回溯法的基本做法是搜索,或是一种组织得井井有条
枚举得有个顺序,否则 轻则有漏的、重复的; 重则无法循环表示。
6
1.按什么顺序去查找所有的解 a.盲目的枚举算法
void main() {
int x[100]; for (x[1]=1;x[1]<=10;x[1]++) for (x[2]=1;x[2]<=10;x[2]++)
for (x[3]=1;x[3]<=10;x[3]++) for (x[4]=1;x[4]<=10;x[4]++) for (x[5]=1;x[5]<=10;x[5]++) for (x[6]=1;x[6]<=10;x[6]++) for (x[7]=1;x[7]<=10;x[7]++) for (x[8]=1;x[8]<=10;x[8]++) if (check(x)==0) { printf(x); }
}
该如何解决冲突的问题呢?
1.行;我们是按照行枚举的,保证了一行一个皇后; 2.列:判断是否存在x[i]=x[j] 3.对角线:主对角线的i-j与从对角线的i+j存在特殊关系,如 图:
八皇后问题(N皇后问题)
⼋皇后问题(N皇后问题)⼋皇后问题,是⼀个古⽼⽽著名的问题,是回溯算法的典型案例。
该问题是国际西洋棋棋⼿马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放⼋个皇后,使其不能互相攻击,即任意两个皇后都不能处于同⼀⾏、同⼀列或同⼀斜线上,问有多少种摆法。
⾸先来看看这张模拟⼋皇后的图。
这张图说明皇后具有横轴、竖轴以及两个斜轴⽅向的杀伤⼒,也就是像⽶字形⼀样;为了减少判断,我们按照⼀个⽅向往另⼀个⽅向排列,中间不能跳⾏,这样我们就可以只判断已经有皇后的位置,还没有皇后的就可以偷懒不⽤判断了。
我的⽅案是:1.从最下⾯开始排列,然后往上添加,从左往右排列,这样就只需要判断⽐⾃⼰Y坐标低的具有杀伤能⼒的位置有没有皇后就OK ⽅法是把⾃⼰假定要放置皇后的位置的X和Y轴都依据判断特性进⾏处理;例如,左斜线X和Y轴都减1;中间的只需要把Y 轴减1;右边的和左边的相反,X轴加1,Y轴减1;注意处理边界问题。
2.为了找到合适的位置我们需要在查找失败的时候具备回溯的能⼒,就需要退回到前⼀⾏(Y=Y-1,注意XY是否到边界),直⾄能回溯或者全部判断完毕,每次回溯的时候记得X轴要从头开始 3.通过⼀个数据结构记录正在查找的⽅案,通过另⼀个数据结构记录已经找到的⽅案,当然也可以⽤⼀个变量记录⽅案个数下⾯这张⿊⾊背景是其中⼀个⽅案的截图,第⼀⾏代表皇后的坐标xy;后⾯的是棋盘,这⾥输出竖轴是x,横轴是y,从上到下,从左到右,其中*是边界,空格是空区,#是皇后。
#include <iostream>#include <cstring>#include "DTString.h"#include "LinkList.h" // 这⾥使⽤链表存储皇后的位置using namespace std;using namespace DTLib;template <int SIZE> // N皇后问题,SIZE表⽰皇后个数或者棋盘⼤⼩class QueenSolution : public Object{protected:enum { N = SIZE + 2 }; // N表⽰棋盘⼤⼩,为了边界识别,棋盘四周都要加⼀格struct Pos : public Object // ⽅位结构体{Pos(int px = 0, int py = 0) : x(px), y(py) { }int x;int y;};int m_chessboard[N][N]; // 棋盘,0表⽰空位,1表⽰皇后,2表⽰边界Pos m_direction[3]; // 共3个⽅向;⽅向-1、-1表⽰左斜线;0、-1表⽰下⽅;1、-1表⽰右斜线;⾸先从最下⽅开始,所以只需考虑下⾯的⾏。
基本算法4-回溯法-N皇后问题
1
x1=1 2
x2= 2 3 kill
1
1 2
回溯到结点2生成结点8, 路径变为(1, 3), 则结点8成为E-结点, 它生成结点9和结点11都会被杀死(即它的儿子表示不可能导 x1=1 致答案的棋盘格局), 所以结点8也被杀死, 应回溯.
1
1
1
2
2
3
x2= 2 x2= 3 3 kill 8 x3=2 9 kill x3=4 11 kill
借书问题 [问题描述]
学校放暑假时,信息学辅导教师有n本书要分给参加培训的n个学生。如:A, B,C,D,E共5本书要分给参加培训的张、刘、王、李、孙5位学生,每人只能选 1本。教师事先让每个人将自己喜爱的书填写在如下的表中,然后根据他们填写的 表来分配书本,希望设计一个程序帮助教师求出可能的分配方案,使每个学生都满 意。 A 张 王 刘 孙 李 Y Y Y Y Y Y Y B C Y D Y E
● ●
……
● ● ● ● ● ● ● ●
……
……
●
●
●
●
●
● ● ● ●
●
●
● ●
●
● ●
●
● ●
● ● ● ●
● ● ● ●
● ● ● ●
● ● ● ● ●
● ● ●
•搜索解空间,剪枝:
– (1) 从空棋盘起,逐行放置棋子。
– (2) 每在一个布局中放下一个棋子,即推演到一 个新的布局。 – (3) 如果当前行上没有可合法放置棋子的位置, 则回溯到上一行,重新布放上一行的棋子。
61 2
64
3
16
3
20
4
22
1
25
JS算法之八皇后问题(回溯法)
JS算法之⼋皇后问题(回溯法)⼋皇后这个经典的算法⽹上有很多种思路,我学习了之后⾃⼰实现了⼀下,现在⼤概说说我的思路给⼤家参考⼀下,也算记录⼀下,以免以后⾃⼰忘了要重新想⼀遍。
⼋皇后问题⼋皇后问题,是⼀个古⽼⽽著名的问题,是回溯算法的典型案例。
该问题是国际西洋棋棋⼿马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放⼋个皇后,使其不能互相攻击,即任意两个皇后都不能处于同⼀⾏、同⼀列或同⼀斜线上,问有多少种摆法。
⼯作原理⾸先从定义知道,两个皇后都不能处于同⼀⾏,所以第0个皇后放在第0⾏,第⼀个皇后放在第1⾏,以此类推。
先在第0⾏第0个格⼦(0,0)放⼀个皇后0,接着把处于同⼀⾏、同⼀列或同⼀斜线上的格⼦都标记为皇后0;然后把皇后1放到第1⾏标记为-1的格⼦中,以此类推直到放下皇后7(即最后⼀个皇后)。
若中途出现放皇后 iQueen时,第 iQueen⾏所有格⼦已经被全部标记,即if( arr[ iQueen*n + i ].index == -1 )的判断,则回溯到上⼀层函数(其实就是没有进⼊到if分⽀,所有没有进⾏递归了,代码执⾏完⾃然会跳回上⼀层函数继续执⾏)。
注意此时的执⾏环境(exection context)已经变了,所有setQueen函数内定义的变量全部回溯到上⼀层函数递归到下⼀层函数前的状态,即执⾏setQueen( iQueen + 1 );这⾏代码前的状态,例如递归前i=2,iQueen=1,⽆论下⼀层函数⾥的i和iQueen怎样变化,回溯后还是i=2,iQueen=1,然后紧接着执⾏未执⾏完的代码。
下⾯是执⾏顺序⼤概的图解:执⾏顺序:1.if-->1.1-->1.2-->1.递归-->2.if-->2.1-->2.2-->2.递归-->3.if-->2.回溯-->1.回溯(前⾯的标号表⽰第⼏层)var n = 8;//总⾏(列)数 8*8var iCount = 0;//n皇后的解法数//arr是长度为n*n的⼀维数组,保存着n*n个对象(li)并有各⾃的坐标,默认index都为-1,表⽰没有被任何皇后标记过 arr[ i*n + j ].y = i; arr[ i*n + j ].x = j;for(var i=0;i<n;i++){for(var j=0;j<n;j++){arr[ i*n + j ].x = j;arr[ i*n + j ].y = i;//arr[ i*n + j ].innerHTML = j + ',' + i;}}//iQueen从0开始,即皇后0function setQueen(iQueen){if( iQueen == n ){iCount++;console.log(iCount)return;}for(var i=0;i<n;i++){if( arr[ iQueen*n + i ].index == -1 ){arr[ iQueen*n + i ].index = iQueen;//arr[ iQueen*n + i ].innerHTML = iQueen;var x = arr[ iQueen*n + i ].x;var y = arr[ iQueen*n + i ].y;for(var j=0;j<arr.length;j++){if( arr[j].index == -1 && (arr[j].x == x || arr[j].y == y || arr[j].x - arr[j].y == x - y || arr[j].x + arr[j].y == x + y) ){arr[j].index = iQueen;//arr[j].innerHTML = iQueen;}}//执⾏到这⾥,就会跳到下⼀层函数中,在执⾏完下⼀层的函数后,才会回溯到上⼀层继续执⾏for循环(此时的for循环是上⼀层的for循环),包括后⾯的所有代码//需要注意的是,例如当前函数的iQueen=1,跳到下⼀层函数 iQueen=2,下⼀层函数执⾏完后,回溯到上⼀层,此时的执⾏环境已经是上⼀层的执⾏环境了,即iQueen是等于1,⽽不是等于2 //递归setQueen( iQueen + 1 );//回溯for(var j=0;j<arr.length;j++){if( arr[j].index == iQueen ){arr[j].index = -1;//arr[j].innerHTML = -1; }}}}}。
用回溯法求解n皇后问题
c程序实现
分析问题
//求解的递归函数 void Queen(int i,int n) { if(i>n) Output(); else { for(int j=1;j<=n;++j) // j代表列值 { int k=1; x[i]=j;//重新换一个列值,这里就是体现回溯的地方 while(k<i) { if((x[k]-x[i])*(abs(x[k]-x[i])-abs(k-i))!=0)
分析问题
问题分析
如何保证任何两个皇后不再一 条斜线上?设两个皇后q1和q2放 在(i,j)和(k,l)位置上,如 果q1和q2在斜率为-1的对角线上, 那么i - j = k - l成立,如果在斜率 为1的对角线上,那么 i + j = k + l成立,由此可知只要 | i - k | ≠ | j - l |成立,q1和q2就不 再同一条斜线上。 |i-k|≠|j-l|
分析问题
2.确定解空间 用完全n叉树表示解空间,现在以n=4为例:
分析问题
问题分析
1
1
2 1 34
3 1 24
4 123
2 34
× ×× ×34 24 23 ×14 13 × 34 ×
√
24 14 12 23 13 12
√
4 3 4 2 3 2 4 3 4 1 3 1 4 2 41 21 3 2 31 2 1
回溯法的基本思想
回溯法的基本思想是在问题的解空间树上按 深度优先搜索策略,从根节点出发搜索整个解 空间。搜索过程中,每到达一个结点时,则判 断该结点为根的子树是否含有问题的解,如果 可以确定该子树中不含有问题的解,则放弃对 该子树的搜索,逐层向其祖先节点回溯。否则, 进入该子树。
Python回溯算法及八皇后问题
Python回溯算法及八皇后问题一、回溯算法1. 回溯算法的概念回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。
如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化来舍弃该解。
回溯算法通常用于解决组合优化问题,如八皇后问题、图的着色问题等。
2. 回溯算法的基本思路回溯算法的基本思路是从一组可能的解中选择一个解,然后递归地对剩下的未解决的问题进行同样的操作。
当一个问题的所有可能解都被找到时,算法结束。
如果在搜索过程中发现当前解不满足约束条件,可以回溯到上一步,尝试其他可能的解。
3. 回溯算法的实现步骤(1)确定问题的解空间,定义一个函数来描述问题的约束条件和目标函数。
(2)使用递归或栈来实现回溯过程。
在每一层递归中,选择一个可能的解,然后递归地处理剩下的未解决的问题。
如果当前解满足约束条件,将其添加到结果集中;否则,回溯到上一步,尝试其他可能的解。
(3)当所有可能的解都被找到时,算法结束。
输出结果集。
二、八皇后问题及其回溯解法1. 八皇后问题的描述八皇后问题是一个简单的组合优化问题,要求在一个8×8的棋盘上放置8个皇后,使得它们互不攻击(即任意两个皇后不在同一行、同一列和同一对角线上)。
这个问题可以用回溯算法求解。
2. 八皇后问题的暴力解法暴力解法是直接枚举所有可能的解,然后检查是否满足约束条件。
这种方法的时间复杂度为O(N!),其中N为皇后的数量(在本问题中为8)。
暴力解法的代码实现如下:```pythondef is_valid(board, row, col):for i in range(row):if board[i] == col or abs(board[i] - col) == abs(i - row):return Falsereturn Truedef solve_n_queens(board, row):if row == len(board):return 1count = 0for col in range(len(board)):if is_valid(board, row, col):board[row] = colcount += solve_n_queens(board, row + 1)return countdef n_queens(n):board = [-1] * nreturn solve_n_queens(board, 0)```3. 八皇后问题的回溯解法及Python实现(1)确定问题的解空间在八皇后问题中,我们可以将棋盘看作是一个一维数组,每个元素表示对应行上放置的皇后所在的列。
回溯法解决n皇后问题
n 皇 后 问 题N 皇后问题,是一个古老而着名的问题,是回溯算法的典型例题:在N*N 格的格子上摆放N 个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法? 1、定义问题的解空间首先以八皇后为例,可以用一棵树表示8皇后问题的解空间。
由于8皇后问题的解空间为8!种排列,因此我们将要构造的这棵树实际上是一棵排列树。
2、确定解空间树的结构给棋盘上的行和列从1到8编号,同时也给皇后从1到8编号。
由于每一个皇后应放在不同的行上,不失一般性,假设皇后i 放在第i 行上,因此8皇后问题可以表示成8元组(x 1, x 2, …, x 8), 其中xi (i =1, 2, …, 8)表示皇后i 所放置的列号。
这种表示法的显式约束条件是S i ={1, 2, 3, 4, 5, 6, 7, 8},i =1, 2, …, 8。
在这种情况下, 解空间为88个8元组组成,而隐式约束条件是没有两个xi 相同(即所有皇后必须在不同列上),且满足不存在两个皇后在同一条对角线上。
加上隐式约束条件,问题的解空间可进一步减小。
此时,解空间大小为8!,因为所有解都是8元组的一个置换。
图5-7表示了8皇后问题的一个解。
图5-7 8皇后问题的一个解为了简单起见,图5-8只给出了n =4时问题的一种可能树结构。
QQQQQQQQ8765432112345678图5-8 4皇后问题解空间的树结构在实际中,并不需要生成问题的整个状态空间。
通过使用限界函数来删除那些还没有生成其所有子结点的活结点。
如果用(x1,x2,…,x i)表示到当前E结点的路径,那么xi+1就是这样的一些结点,它使得(x1,x2,…,x i,x i+1)没有两个皇后处于相互攻击的棋盘格局。
在4皇后问题中,惟一开始结点为根结点1,路径为( )。
开始结点既是一个活结点,又是一个E结点,它按照深度优先的方式生成一个新结点2,此时路径为(1),这个新结点2变成一个活结点和新的E结点,原来的E结点1仍然是一个活结点。
N皇后问题—回溯算法经典例题
N皇后问题—回溯算法经典例题题⽬描述: N 皇后是回溯算法经典问题之⼀。
问题如下:请在⼀个 n×n 的正⽅形盘⾯上布置 n 名皇后,因为每⼀名皇后都可以⾃上下左右斜⽅向攻击,所以需保证每⼀⾏、每⼀列和每⼀条斜线上都只有⼀名皇后。
题⽬分析: 在 N 皇后问题中,回溯算法思路是每⼀次只布置⼀个皇后,如果盘⾯可⾏,就继续布置下⼀个皇后。
⼀旦盘⾯陷⼊死局,就返回⼀步,调整上⼀个皇后的位置。
重复以上步骤,如果解存在,我们⼀定能够找到它。
可以看到,我们在重复“前进—后退—前进—后退”这⼀过程。
问题是,我们不知道⼀共需要重复这个过程多少次,也不能提前知道 n 是多少,更不知道每⼀次后退时需要后退⼏⾏,因此我们不能利⽤ for 循环和 while 循环来实现这个算法。
因此我们需要利⽤递归来实现代码结构。
逻辑如下:当⽅法布置完当前⾏的皇后,就让⽅法调⽤⾃⼰去布置下⼀⾏的皇后。
当盘⾯变成绝境的时候,就从当前⽅法跳出来,返回到上⼀⾏,换掉上⼀⾏的皇后再继续。
我们定义 NQueens(n) ⽅法,它负责输出所有成⽴的 n×n 盘⾯。
其中 1 代表皇后,0 代表空格。
代码:def NQueens(n): #输出所有成⽴的n·n盘⾯cols = [0 for _ in range(n)] #每⼀⾏皇后的纵坐标res = [] #结果列表def checkBoard(rowIndex): #检查盘⾯是否成⽴,rowIndex是当前⾏数for i in range(rowIndex):if cols[i]==cols[rowIndex]: #检查竖线return Falseif abs(cols[i]-cols[rowIndex]) == rowIndex-i: #检查斜线return Falsereturn Truedef helper(rowIndex): #布置第rowIndex⾏到最后⼀⾏的皇后if rowIndex==n: #边界条件board = [[0 for _ in range(n)] for _ in range(n)]for i in range(n):board[i][cols[i]] = 1res.append(board) #把当前盘⾯加⼊结果列表return#返回for i in range(n): #依次尝试当前⾏的空格cols[rowIndex] = iif checkBoard(rowIndex): #检查当前盘⾯helper(rowIndex+1) #进⼊下⼀⾏helper(0) #从第1⾏开始return resprint(NQueens(4))代码分析: 在 NQueens() ⽅法中,我们会定义 helper(x) ⽅法帮助实现递归结构。
八皇后问题的n种解法
⼋皇后问题的n种解法经典的⼋皇后问题:在8×8格的国际象棋上摆放⼋个皇后,使其不能互相攻击,即任意两个皇后都不能处于同⼀⾏、同⼀列或同⼀斜线上,问有多少种摆法。
很早就接触过⼋皇后问题,最近数据结构作业中⼜看到了这个题⽬,仔细研究了⼀波⽹上诸位⼤⽜的博客,发现这个问题居然有这么多有趣的优化。
1.经典的回溯递归解法:#include<stdio.h>#include<iostream>using namespace std;//dfs,每⾏只能放⼀个元素,遍历每⾏的每个位置,⽤⼀个⼀维数组记录,最后检查是否符合要求int ans;int vis[10];int abs(int x){return x > 0 ? x : -x;}bool check(int r,int c){for(int i = 1;i<r;i++){if(vis[i] == c) return false;if(vis[i] - c == r - i || vis[i] - c == i - r) return false;}return true;}void dfs(int r){if(r > 8){ans++;return;}for(int i = 1;i<=8;i++){if(check(r,i)){vis[r] = i;dfs(r+1);vis[r] = 0;}}}main(){dfs(1);cout<<ans<<endl;}2.对⾓线检查优化/*⽤三个数组记录列,左对⾓线,右对⾓线信息,每次判断该位置是否符合要求,只在符合要求位置放置元素。
*/#include <iostream>using namespace std;const int maxn=105;const int mo=100;typedef long long ll;int a[maxn],n = 8,ans=0;bool b[maxn],c[maxn],d[maxn];void sou(int x){if(x > n){ans++;return;}for(int i = 1;i <= n;i++)if(!(b[i] || c[x+i] || d[x-i+n])){b[i] =c [x+i]=d[x-i+n]=true;a[x] = i;sou(x+1);b[i] =c [x+i] = d[x-i+n]=false;}}int main(){sou(1);cout<<ans;}3.位运算://算法思想与上⼀相同,改⽤三个int来存储信息,采⽤位运算提取合适位置#include<iostream>#include<stdio.h>using namespace std;int board;int n;int ans = 0;void n_queen(int col,int ld,int rd){if(col == board){ans++;return;}int pos = board & (~(col | ld | rd));while(pos){int p = pos & (-pos);pos = pos - p;n_queen(col | p , (ld | p) << 1,(rd | p) >> 1);}}int main(){cin>>n;board = (1 << n) - 1;n_queen(0,0,0);cout<<ans<<endl;}4.⼗⾏内的⼋皇后...对知乎上各路⼤⼤的炫技佩服的五体投地,更改了⼀下上⼀代码,勉强也达到了⼗⾏的要求。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
回溯算法与八皇后问题(N皇后问题)1 问题描述八皇后问题是数据结构与算法这一门课中经典的一个问题。
下面再来看一下这个问题的描述。
八皇后问题说的是在8*8国际象棋棋盘上,要求在每一行放置一个皇后,且能做到在竖方向,斜方向都没有冲突。
更通用的描述就是有没有可能在一张N*N的棋盘上安全地放N个皇后?2 回溯算法回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。
回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。
在现实中,有很多问题往往需要我们把其所有可能穷举出来,然后从中找出满足某种要求的可能或最优的情况,从而得到整个问题的解。
回溯算法就是解决这种问题的“通用算法”,有“万能算法”之称。
N皇后问题在N增大时就是这样一个解空间很大的问题,所以比较适合用这种方法求解。
这也是N皇后问题的传统解法,很经典。
下面是算法的高级伪码描述,这里用一个N*N的矩阵来存储棋盘:1) 算法开始, 清空棋盘,当前行设为第一行,当前列设为第一列2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步3) 在当前位置上满足条件的情形:在当前位置放一个皇后,若当前行是最后一行,记录一个解;若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;若当前行是最后一行,当前列不是最后一列,当前列设为下一列;若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;以上返回到第2步4) 在当前位置上不满足条件的情形:若当前列不是最后一列,当前列设为下一列,返回到第2步;若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置,返回到第2步;算法的基本原理是上面这个样子,但不同的是用的数据结构不同,检查某个位置是否满足条件的方法也不同。
为了提高效率,有各种优化策略,如多线程,多分配内存表示棋盘等。
为了便于将上述算法编程实现,将它用另一种形式重写:Queen()Loop:if check_pos(curr_row, curr_col) == 1 thenput_a_queen(curr_row, curr_col);if curr_row == N thenrecord_a_solution();end if;if curr_row != N thencurr_row = curr_row + 1;curr_col = 1;elseif curr_col != N thencurr_col = curr_col + 1;elsebacktrack();end if;end if;elseif curr_col != N thencurr_col = curr_col +1;elsebacktrack();end if;end if;end Queen;3 实现3.1 数据结构这里,用一个N个元素的一维数组来表示。
数组的下标表示棋盘的行,数组的元素表示该行皇后所在的列。
这个结构自然就消除了列冲突,因为每一行只有一个皇后。
还有利用它解决行冲突,也很简单,只要比较各个元素就行了,看有没有相等的元素。
斜线冲突也简单,因为同一斜线上的在一直线上,该直线的斜率为+1(左下至右上)或-1(左上至右下),所以,左下至右上的冲突检测方法为看式子是否成立(row –curr_row)/(col –curr_col) = 1,也就是(row - col) = (curr_row –curr_col),相同,则有冲突。
同理,左上至右下看式子(row + col) = (curr_row +curr_col)。
这里,也是看下标与其对应元素的和与差是不是与要检测位置的相应结果相同,如果有一个相同,就有冲突。
数据结构一确定,冲突检测方法就有了,关键的部分的算法就完成了。
3.2 代码/*************************************************************** File: NQueen.cDescripton: N皇后问题求解之回溯版Author: liuqh, Hunan UniversityDate: 2007.5.22, Tues.Version: 1.0**************************************************************/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <math.h>#define SOLS(n) (int)(pow(2.0, (double)(n)))#define DEBUG/*************************全局变量定义**************************/int N = -1; /*棋盘的大小*/char *p_board = NULL; /*指向棋盘,这里是用一个长度为N 的一维数组表示的*/char *buf = NULL; /* 输出缓冲 */int buf_index = -1;/*********************************本编译单元的函数声明*********************************/static int queen(FILE *fp);static int clear_board(int i);static int check_pos(int curr_row);static int record_a_solution(FILE *fp, int solutions);static int backtrack(int *p_curr_row);static void flush_buf(FILE *fp, int solutions);/*** 函数:main()** 功能:程序入口** 入口: 参数个数(整型);程序名与N的大小** 出口:** 返回:成功返回0,否则返回-1** 备注:*/int main(int argc, char **argv){clock_t start, finish;double duration;int solutions;FILE *fp;/* 命令行参数检查*/if (argc != 2){fprintf(stderr, "用法: queen N\n") ;return -1;}N = atoi(argv[1]);if (N <= 0){fprintf(stderr, "用法: queen N \n N必须大于0\n") ;return -1;}/*开始计时*/start = clock();p_board = (char*)calloc(N, sizeof(char));if (NULL == p_board){fprintf(stderr, "为棋盘分配内存失败\n") ;return -1;}fp = NULL;if ((fp = fopen("solutions.txt", "w")) == NULL){free(p_board);fprintf(stderr, "创建文件solutions.txt失败!\n") ;return -1;}solutions = queen(fp);if (-1 == solutions){free(p_board);if (fclose(fp) == EOF){fprintf(stderr, "关闭文件solutions.txt失败!\n") ;return -1;}fprintf(stderr, "调用queen()失败\n") ;return -1;}printf("共有%d种解决方案,具体结果请查看文件solutions.txt\n", solutions) ;fprintf(fp, "solutions = %d\n", solutions);free(p_board);/*操作完成,计时结束*/finish = clock();/*计算时间间隔,以秒为单位*/duration = (double)(finish - start) / CLOCKS_PER_SEC;printf("计算完毕,共耗时%f秒!\n", duration);fprintf(fp, "time = %f\n", duration);if (fclose(fp) == EOF){fprintf(stderr, "关闭文件solutions.txt失败!\n") ;return -1;}return 0;}/*** 函数:queen()** 功能:计算N皇后问题的解** 入口: 记录结果的文件指针** 出口:** 返回:成功返回解的个数,出错返回-1** 备注:*/int queen(FILE *fp){int curr_row;int result;result = 0;if (clear_board(0) == -1){fprintf(stderr, "清空棋盘失败!\n") ;return -1;}curr_row = 0;p_board[curr_row] = (char)0;buf = (char*)calloc(SOLS(N)*N, sizeof(char));if (NULL == buf){fprintf(stderr, "为解分配内存失败\n") ;return -1;}while (1){if (check_pos(curr_row)== 1){if (N-1 == curr_row){result++;record_a_solution(fp, result);}if (curr_row != N-1){curr_row++;p_board[curr_row] = (char)0;continue;}}if (p_board[curr_row] != (char)(N-1)){p_board[curr_row] ++;}else{if (backtrack(&curr_row) == 1){break;}}} /* end while */flush_buf(fp, result);free(buf);return result;}/*** 函数:clear_board()** 功能:初始化棋盘** 入口: 初始化开始的行(整型), 全局变量p_board, N ** 出口:初始化后的棋盘,这里指指针p_board指向的数组** 返回:成功返回0,出错返回-1** 备注:*/int clear_board(int i){if (memset(&p_board[i], -1, N-i) == NULL){return -1;}return 0;}** 函数:check_pos()** 功能:检查某个位置是否能放一个皇后** 入口: 位置所在的行(整型), 全局变量p_board, N** 出口:** 返回:可以放置的话返回1,否则返回0** 备注:*/int check_pos(int curr_row){int i;int sum, sub;/*检查是否有行冲突*/for (i = 0; i < curr_row; i++){if (p_board[i] == p_board[curr_row]){return 0;}}/*检查斜线方向的冲突*/sum = curr_row + p_board[curr_row];sub = curr_row - p_board[curr_row];for (i = 0; i < curr_row; i++){if (i+p_board[i] == sum || i - p_board[i] == sub){return 0;}}return 1;}** 函数:record_a_solution()** 功能:记录一个合法的解** 入口:记录结果的文件指针;解的个数(整型)全局变量p_board, N** 出口:存储解的缓存buf(字符串)** 返回:成功,返回0,否则返回-1** 备注:解先缓存在内存中,这样快一点,要提高速度,修改SOLS宏以获得更大的缓冲区*/int record_a_solution(FILE *fp, int solutions){if (++buf_index != SOLS(N)){if (memcpy(&buf[buf_index*N], p_board, N) == NULL){fprintf(stderr, "拷贝解到缓冲时出错!\n") ;buf_index--;return -1;}}else{flush_buf(fp, solutions);}return 0;}/*** 函数:backtrack()** 功能:回溯到上一行** 入口: 全局变量p_board, N** 出口:当前行(整型指针)** 返回:可以回溯,返回0,否则返回1,表示算法可以退出了** 备注:可能递归调用自身*/int backtrack(int *p_curr_row){if (0 == *p_curr_row){if (clear_board(p_board[*p_curr_row]) == -1){fprintf(stderr, "backtrack():清空棋盘失败!\n") ;}return 1; }if (clear_board(p_board[*p_curr_row]) == -1){fprintf(stderr, "backtrack():清空棋盘失败!\n") ;return -1;}(*p_curr_row)--;if (p_board[*p_curr_row] != N-1){p_board[*p_curr_row]++;return 0;}else{return backtrack(p_curr_row);}}/*** 函数:flush_buf()** 功能:刷新缓冲区** 入口: 记录结果的文件指针;当前全部解的个数(整型);全局变量buf,buf_index, N** 出口:** 返回:** 备注:*/void flush_buf(FILE *fp, int solutions){int k,i, j;for (k = 0; k <= buf_index; k++){fprintf(fp, "solutions %d\n",solutions - (buf_index - k));fprintf(fp, "--------------------------------------\ n");for (i = 0; i < N; i++){for (j = 0; j < N; j++){fprintf(fp, "%3c", j == buf[k*N+i] ? 'Q' : 'x');}fprintf(fp, "\n");}fprintf(fp, "--------------------------------------\ n");}buf_index = -1;}(注:可编辑下载,若有不当之处,请指正,谢谢!)。