一步一步写算法(之八皇后)

合集下载

算法——八皇后问题(eightqueenpuzzle)之回溯法求解

算法——八皇后问题(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;}。

八皇后问题递归算法

八皇后问题递归算法

八皇后问题递归算法八皇后问题是一个经典的数学问题,其目标是在一个8×8的棋盘上放置8个皇后,使得没有两个皇后位于同一行、同一列或同一斜线上。

这个问题可以通过递归算法来求解,本文将详细介绍八皇后问题的递归算法及其实现过程。

我们需要定义一个函数来判断当前位置是否可以放置皇后。

该函数的输入参数为当前行和当前列,输出为一个布尔值,表示该位置是否可以放置皇后。

具体实现如下:```bool isSafe(int board[8][8], int row, int col){int i, j;// 检查当前列是否有其他皇后for (i = 0; i < row; i++)if (board[i][col])return false;// 检查左上方是否有其他皇后for (i = row, j = col; i >= 0 && j >= 0; i--, j--)if (board[i][j])return false;// 检查右上方是否有其他皇后for (i = row, j = col; i >= 0 && j < 8; i--, j++)if (board[i][j])return false;return true;}```接下来,我们可以使用递归算法来解决八皇后问题。

递归算法的思想是将问题分解为子问题,然后逐步解决子问题,最终得到原问题的解。

具体的递归算法如下:```bool solveNQueens(int board[8][8], int row){// 所有行都已经放置好了皇后,得到了一个解if (row == 8)return true;// 尝试在当前行的每个列中放置皇后for (int col = 0; col < 8; col++)// 检查当前位置是否可以放置皇后if (isSafe(board, row, col)){// 放置皇后board[row][col] = 1;// 递归求解下一行if (solveNQueens(board, row + 1))return true;// 回溯,撤销当前位置的皇后board[row][col] = 0;}}// 无法找到解return false;}```我们可以编写一个主函数来调用递归算法并打印结果。

八皇后问题详细的解法

八皇后问题详细的解法

检查合格的才继续向下扩展; 遇到不合格的“掉头就走”。 回溯法指导思想——走不通,就掉头
14


只要当前枚举到的状态可行,就继续枚举下去。 当找到一种方案或者无法继续枚举下去时,就退 回到上一状态。退回到上一状态的过程叫做回溯, 枚举下一个状态的过程叫做递归。 回溯就是像人走迷宫一样,先选择一个前进方向 尝试,一步步试探,在遇到死胡同不能再往前的 时候就会退到上一个分支点,另选一个方向尝试, 而在前进和回撤的路上都设置一些标记,以便能 够正确返回,直到达到目标或者所有的可行方案 都已经尝试完为止。
八皇后问题
1

1八皇后问题背景 2盲目的枚举算法 3加约束的枚举算法 4回溯法及基本思想 5 回溯法应用 6八皇后问题的递归回溯算法 7八皇后问题的非递归回溯算法
2
【背景】 八皇后问题是一个以国际象棋为背 景的问题: 如何能够在 8×8 的国际象棋棋盘上 放置八个皇后,使得任何一个皇后都 无法直接吃掉其他的皇后?为了达到 此目的,任两个皇后都不能处于同一 条横行、纵行或斜线上。

违规的情况可以整合改写为: abs(xi - xj)=abs( i-j )) or (xi = xj)
9
queen1( )盲目的枚举算法 check1(a[],n) { 用a[1]~a[8]存储x1~x8 {int i,j; int a[9]; for(i=2;i<=n;i++) for (a[1]=1;a[1]<=8;a[1]++) for(j=1;j<=i-1;j++) for (a[2]=1;a[2]<=8;a[2]++) if (a[i]==a[j]) or for (a[3]=1;a[3]<=8;a[3]++) (abs(a[i]-a[j])==abs(i-j) for (a[4]=1;a[4]<=8;a[4]++) return(0); for (a[5]=1;a[5]<=8;a[5]++) for (a[6]=1;a[6]<=8;a[6]++) return(1); } 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 }

经典算法-(五)八皇后问题

经典算法-(五)八皇后问题

经典算法-(五)⼋皇后问题简介:问题,是⼀个古⽼⽽著名的问题,是的典型案例。

该问题是国际西洋棋棋⼿马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放⼋个皇后,使其不能互相攻击,即任意两个皇后都不能处于同⼀⾏、同⼀列或同⼀斜线上,问有多少种摆法。

认为有76种⽅案。

1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有⼈⽤图论的⽅法解出92种结果。

计算机发明后,有多种计算机语⾔可以解决此问题。

问题描述:⼋皇后问题是⼀个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置⼋个皇后,使得任何⼀个皇后都⽆法直接吃掉其他的皇后?为了达到此⽬的,任两个皇后都不能处于同⼀条横⾏、纵⾏或斜线上。

⼋皇后问题可以推⼴为更⼀般的n皇后摆放问题:这时棋盘的⼤⼩变为n1×n1,⽽皇后个数也变成n2。

⽽且仅当 n2 = 1 或 n1 ≥ 3 时问题有解。

java实现:public class Queen{//同栏是否有皇后,1表⽰有private int[] column;//右上⾄左下是否有皇后private int[] rup;//左上⾄右下是否有皇后private int[] lup;//解答private int[] queen;//解答编号private int num;public Queen(){column=new int[8+1];rup=new int[(2*8)+1];lup=new int[(2*8)+1];for(int i=1;i<=8;i++)column[i]=0;for(int i=1;i<=(2*8);i++)rup[i]=lup[i]=0; //初始定义全部⽆皇后queen=new int[8+1];}public void backtrack(int i){if(i>8){showAnswer();}else{for(int j=1;j<=8;j++){if((column[j]==0)&&(rup[i+j]==0)&&(lup[i-j+8]==0)){//这⾥右上到左下是左边开始数的,并且是从2开始计数的总共8个正好到9,⽽左上到右下是做右边开始数的所以开头是8.明显是从1开始计数的。

回溯法 八皇后

回溯法 八皇后

回溯法(8皇后问题)1.1算法原理回溯算法实际是一个类似枚举的搜索尝试方法,其基本思想是在搜索尝试中找问题的解,采用了一种“走不通就掉头”的思想,作为其控制结构。

从一条路往前走,能进则进,不能进则退回来,换一条路再试。

八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有符合条件的位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。

回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。

回溯算法说白了就是穷举法。

不过回溯算法使用剪枝函数,剪去一些不可能到达最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。

回溯法是一个既带有系统性又带有跳跃性的的搜索算法。

它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。

算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。

如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。

否则,进入该子树,继续按深度优先的策略进行搜索。

回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。

而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。

用回溯法搜索解空间树时,通常采用两种策略来避免无效搜索,提高回溯法的搜索效率。

其一是用约束函数在扩展结点处剪去不满足约束的子树;其二是用限界函数剪去不能得到最优解的子树。

1.2算法适用性它适用于解一些组合数较大的问题,有条件限制的枚举,即优化的枚举.1.3算法描述8皇后回溯算法1.设Column[8]数组依次存储第一行到第八行的列位置,QUEEN_NUM=8,皇后个数2.从第一行第一列开始放置,开始放置下一行的皇后(当且仅当前行的皇后位置正确时)3.判断放置位置是否合理:Column[i]==Column[n],判断是否在同一列,abs(Column[i]-Column[n])==(n-i)),判断时候在斜线上。

4八皇后问题

4八皇后问题

实验四八皇后问题
一、目的和要求
1. 掌握递归的使用方法。

2. 掌握回溯法。

二、实验内容
八皇后问题是十九世纪著名的数学家高斯提出的:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻(任意两个皇后都不能处于同一行、同一列或同一斜线上),问有多少种摆法?输出所有的皇后摆法。

三、算法:
提示:
1.皇后q1(x1,y1)和皇后q2(x2,y2)相互吃掉的条件:
x1 == x2 || y1 == y2 || x1 – x2 == y1 – y2 || x1 – x2 == y2 – y1
2.用回溯法解决八皇后问题的步骤为:
用一个数组存储当前成功摆放的皇后位置,以便下一列摆放皇后时进行安全判断。

因为一列(行)只能摆放一个皇后,否则会互相吃掉,所以可以用列(行)号来作为递归函数的n
1)从第1列开始,为皇后找到安全位置,然后递归进行下一列。

2) 如果在第8列上找到了安全位置,则棋局成功,输出此种摆放下所有皇后的位置信息。

3) 如果在第n列出现死胡同(在所有位置都不能安全摆放皇后了),则后退到上一列进行回溯(如果用递归实现,该回溯是递归调用自动完成)。

程序请自行设计。

八皇后问题—经典回溯算法

八皇后问题—经典回溯算法

⼋皇后问题—经典回溯算法⼋皇后问题⼋皇后问题,是⼀个古⽼⽽著名的问题,是回溯算法的典型案例。

该问题是国际西洋棋棋⼿马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放⼋个皇后,使其不能互相攻击,即任意两个皇后都不能处于同⼀⾏、同⼀列或同⼀斜线上,问有多少种摆法。

⾼斯认为有76种⽅案。

1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有⼈⽤图论的⽅法解出92种结果。

回溯算法思想回溯算法的基本思想是:从⼀条路往前⾛,能进则进,不能进则退回来,换⼀条路再试。

⼋皇后问题就是回溯算法的典型,第⼀步按照顺序放⼀个皇后,然后第⼆步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第⼀个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。

回溯在迷宫搜索中使⽤很常见,就是这条路⾛不通,然后返回前⼀个路⼝,继续下⼀条路。

回溯算法说⽩了就是穷举法。

不过回溯算法使⽤剪枝函数,剪去⼀些不可能到达最终状态(即答案状态)的节点,从⽽减少状态空间树节点的⽣成。

回溯法是⼀个既带有系统性⼜带有跳跃性的的搜索算法。

它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。

算法搜索⾄解空间树的任⼀结点时,总是先判断该结点是否肯定不包含问题的解。

如果肯定不包含,则跳过对以该结点为根的⼦树的系统搜索,逐层向其祖先结点回溯。

否则,进⼊该⼦树,继续按深度优先的策略进⾏搜索。

回溯法在⽤来求问题的所有解时,要回溯到根,且根结点的所有⼦树都已被搜索遍才结束。

⽽回溯法在⽤来求问题的任⼀解时,只要搜索到问题的⼀个解就可以结束。

这种以深度优先的⽅式系统地搜索问题的解的算法称为回溯法,它适⽤于解⼀些组合数较⼤的问题。

⼋皇后实现⼆以下实现是极客时间王争的解法,⾮常巧妙,思路也⾮常清晰,如果理解了⼋皇后问题的本质后建议采⽤该⽅法,代码实现如下:#include <iostream>int queenPlace[8] = { 8 }; //全局变量,下标表⽰⾏,值表⽰queen存储在那⼀列int count = 0; //计数器void printQueen() { //打印⼀个⼆维数组for (int i = 0; i < 8; ++i) {for (int j = 0; j < 8; ++j) {if (queenPlace[i] == j) {printf("Q ");} else {printf("* ");}}printf("\n");}printf("----count:%d-----\n", ++count);}bool isOk(int row, int col) { //判断row⾏col列放置是否合适int leftUp = col - 1; //左上对⾓线int rightUp = col + 1; //右上对⾓线for (int i = row - 1; i >= 0; --i) {if (queenPlace[i] == col) return false; //同列上的格⼦有皇后if (leftUp >= 0) {if (queenPlace[i] == leftUp) return false; //左上对⾓线有皇后}if (rightUp < 8) {if (queenPlace[i] == rightUp) return false; //右上对⾓线有皇后}--leftUp; ++rightUp;}return true;}void eightQueen(int row) {if (row == 8) { //8个皇后都放置好,打印,⽆法递归返回printQueen();return;}for (int col = 0; col < 8; ++col) { //每⼀⾏都有8种⽅法if (isOk(row, col)) { //满⾜要求queenPlace[row] = col; //第row⾏的皇后放在col列eightQueen(row+1); //考察下⼀⾏}}}int main() {eightQueen(0);return0;class Solution {public:vector<vector<string>> res;vector<int> n_queen;vector<vector<string>> solveNQueens(int n) {n_queen.resize(n);backtrack(0);return res;}void backtrack(int row) {if (row == n_queen.size()) {storeResult();return;}for (int i = 0; i < n_queen.size(); ++i) {if (!isOk(row, i)) continue;n_queen[row] = i;backtrack(row + 1);}}bool isOk(int row, int col) {int left_up = col - 1;int right_up = col + 1;for (int i = row - 1; i >= 0; --i) {if (n_queen[i] == col // 当前列|| n_queen[i] == left_up-- // 左上对⾓,⽆需判断 left_up < 0, 该情况不会成⽴的 || n_queen[i] == right_up++) { // 右上对⾓,⽆需判断 right_up > n_queen.size() return false;}}return true;}void storeResult() {vector<string> result;for (auto i : n_queen) {string s(n_queen.size(), '.');s[i] = 'Q';result.push_back(s);}res.push_back(result);}};解法2:class Solution {public:vector<bool> col;vector<bool> dia1;vector<bool> dia2;vector<vector<string>> result;vector<string> generateQueen(vector<int>& q){vector<string> res;for (int i = 0; i < q.size(); ++i){string s(q.size(), '.');s[q[i]] = 'Q';res.push_back(s);}return res;}void traceBack(int n, int row, vector<int>& q){if (row == n) {result.push_back(generateQueen(q));return;}for (int i = 0; i < n; ++i){if (!col[i] && !dia1[row + i] && !dia2[row - i + n - 1]){q.push_back(i);col[i] = true;dia1[row + i] = true;dia2[row - i + n - 1] = true;traceBack(n, row + 1, q);col[i] = false;dia1[row + i] = false;dia2[row - i + n - 1] = false;q.pop_back();}}vector<vector<string>> solveNQueens(int n) { col = vector<bool>(n, false);dia1 = vector<bool>(2 * n - 1, false);dia2 = vector<bool>(2 * n - 1, false);vector<int> q;traceBack(n, 0, q);return result;}};。

八皇后问题(N皇后问题)

八皇后问题(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表⽰右斜线;⾸先从最下⽅开始,所以只需考虑下⾯的⾏。

八皇后问题详细的解法

八皇后问题详细的解法

若无法放下皇后则回到上一行; 即回溯
当n行的皇后都已确定后;我们就 找到了一种方案
check2 int a ;int n
queen21例1 b加约束的枚举算法{//i多nt次i; 被调用;只是一重循环
{int a9; for a1=1;a1<=8;a1++ for a2=1;a2<=8;a2++
fori=1;i<=n-1;i++ if absai-an==absi-n or ai==an
}}}}}}}
此算法可读性很好;体 现了“回溯”&但它只 能解决八皇后问题;而 不能解决任意的n皇后 问题&
18
2 回溯法应用-算法说明
八皇后问题中的核心代码: 遍历过程函数; check函数&
解决此类问题的核心内容: 解空间树的搜索算法; 估值/判断函数:判断哪些状态适合继续扩展;或者作为 答案状态&
按什么顺序去搜? 目标是没有漏网之鱼;尽量速度快&
5
2 问题设计盲目的枚举算法
a 盲目的枚举算法
通过8重循环模拟搜索空间中的88个状态;
按枚举思想;以DFS的方式;从第1个皇后在第1列开始搜 索;枚举出所有的“解状态”:
从中找出满足约束条件的“答案状态”&
约束条件?
枚举得有个顺序;否则轻 则有漏的、重复的;重 则无法循环表示&
9
queen1 盲目的枚举算法
{ int a9;
用a1~a8存储x1~x8
for a1=1;a1<=8;a1++
for a2=1;a2<=8;a2++
for a3=1;a3<=8;a3++

八皇后问题最简单算法

八皇后问题最简单算法

八皇后问题最简单算法
八皇后问题最简单算法是使用回溯法。

1. 回溯法在求解八皇后问题时,会生成一个8位的二进制数,每一位代表一列是否放置皇后。

如果某一列放置了皇后,则该位为1,否则为0。

2. 在放置皇后时,如果当前位置可以放置皇后,则尝试放置。

如果放置后当前位置形成了一个合法的棋盘,则继续递归地放置下一个皇后。

如果放置后形成了一个不合法的棋盘,则回溯到上一个状态,尝试其他位置。

3. 如果所有8个皇后都放置完毕,且形成了一个合法的棋盘,则找到了一个解。

以上信息仅供参考,如需了解更多信息,建议查阅八皇后问题相关书籍或咨询专业人士。

JS算法之八皇后问题(回溯法)

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; }}}}}。

八皇后问题详细的解法

八皇后问题详细的解法
八皇后问题
1
1八皇后问题背景 2盲目的枚举算法 3加约束的枚举算法 4回溯法及基本思想 5 回溯法应用 6八皇后问题的递归回溯算法 7八皇后问题的非递归回溯算法
2
【背景】 八皇后问题是一个以国际象棋为背
景的问题: 如何能够在 8×8 的国际象棋棋盘上
放置八个皇后,使得任何一个皇后都 无法直接吃掉其他的皇后?为了达到 此目的,任两个皇后都不能处于同一 条横行、纵行或斜线上。
}
}
23
20
2 回溯法应用-算法框架-递归算法框架
int a[n]; Queens(int k) { if (k>n) 即表示最后一个皇后摆放完毕,输出结果;
else for(i=下界 ; i<=上界; i++) //枚举K个皇后所有可能的路径 {依次从列顶端开始搜索,一直到列底端,直到找到合适位置,如
果未找到,自动返回上层递归
的,能避免不必要搜索的穷举式搜索法。这种方法适 用于解一些组合数相当大的问题。 回溯法在问题的解空间树中,按深度优先策略,从根 结点出发搜索解空间树。算法搜索至解空间树的任意 一点时,先判断该结点是否包含问题的解。如果肯定 不包含,则跳过对该结点为根的子树的搜索,逐层向 其祖先结点回溯;否则,进入该子树,继续按深度优 先策略搜索。
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]); }
题,而不能解决任意
}}}}}}}

八皇后问题算法分析

八皇后问题算法分析

开始构造界面和按钮控件,声明二维数组,表头数组和变量i=0,j,q.判断变量i的值是否不大于8是产生随机数q是判断表头数组number【q】的值是否为1i++否否在与界面相关的二维数组中德a【i】【q】位置打印“K”,并且给number【q】赋值为1结束流程图八皇后问题算法分析在这个问题中首先定义的是一个用于构造界面的二位数组a【i】【j】和一个用于判断的表头数组number【】。

在开始进行八皇后棋子排列的时候,首先对行进行递增循环,即i初始值为0,每次i++,i最大值为8的循环。

在每次循环中产生一个小于8的随机数q,然后判断表头数组number【】中number【q】位置的值是否为1,如果不是,则在二维数组a【i】【q】位置上打印表示棋子的“K”;如果为1,则返回产生随机数的步骤继续产生随机数。

在循环到i>8时,跳出循环,这时候一个完整的八皇后排列也就出来了。

源代码:packagequeen;importjava.awt.*;importjava.awt.event.*;classequeenextendsFrameimplementsActionListener{//构造界面和定义数组Buttonenter;Buttonclean;Buttonexit;intnumber[] =newint[8];inti,j,q;Labela[][] =newLabel[8][8];equeen(String s){GridLayout grid;grid =newGridLayout(9,8);setLayout(grid);enter=newButton("begin");clean=newButton("clean");exit=newButton("esit");for(inti = 0;i<8;i++){}for(inti = 0;i<8;i++){number[i] = 0;//初始化判断数组}add(enter);add(clean);add(exit);enter.addActionListener(this);clean.addActionListener(this);exit.addActionListener(this);setBounds(100,100,300,300);for(intj = 0;j<8;j++){}a[i][j] =newLabel();if((i+j)%2==0)a[i][j].setBackground(Color.yellow);elsea[i][j].setBackground(Color. gray);add(a[i][j]);setVisible(true);validate();}publicvoidactionPerformed(ActionEvent e){if(e.getSource()==enter){}for(inti =0;i<8;i++){}for(inti =0;i<8;i++){}for(inti = 0;i<8;i++){}number[i] = 0;while(true){}q= (int)(Math.random()*8);if(number[q]==0){}elseif(number[q]!=0)continue;a[i][q].setText("K");number[q] = 1;break;for(intj=0;j<8;j++){}a[i][j].setText("");}}if(e.getSource()==clean){}if(e.getSource()==exit){ }System.exit(0);for(inti = 0;i<8;i++){}for(intj = 0;j<8;j++){}a[i][j].setText("");publicclassqueen {}publicstaticvoidmain(String[] args){ newequeen("八皇后问题");}。

算法入门经典-第七章例题7-2八皇后问题

算法入门经典-第七章例题7-2八皇后问题

算法⼊门经典-第七章例题7-2⼋皇后问题原本利⽤回溯思想解决的经典⼋皇后问题,其实也是可以⽤递归解决的~⼋皇后的递归解决思路:从第⼀⾏开始,依次判断0~8列的哪⼀列可以放置Queen,这样就确定了该⾏的Queen的位置,然后⾏数递增,继⽽递归实现下⼀⾏的判断,依次类推直到⾏数增加到8(⾏数从0开始的),此时为递归-----归的条件,即表⽰⼀种⼋皇后的解决⽅法完成,打印结果;之后进⾏下⼀种解决⽅法的寻找,⼤致思路个⼈理解是这样noDanger(row,j,(*chess)[8])函数是判断第row⾏第j列是否可以放置Queen#include<stdio.h>int count=0;//参数row:起始⾏//参数n:表⽰列数//参数(*chess)[8]表⽰指向棋盘每⼀⾏的指针int NotDanger(int row,int j,int (*chess)[8])//⽐较不同⾏同列上是否有其他皇后{int i,k,flag1=0,flag2=0,flag3=0,flag4=0,flag5=0;//判断列⽅向for(i=0;i<8;i++){if(*(*(chess+i)+j)!=0) //在这之前列上有其他皇后{flag1=1;break;}}for(i=row,k=j;i>=0&&k>=0;i--,k--){if(*(*(chess+i)+k)!=0) //左上⽅{flag2=1;break;}}for(i=row,k=j;i<8&&k<8;i++,k++){if(*(*(chess+i)+k)!=0) //右下⽅{flag3=1;break;}}for(i=row,k=j;i>=0&&k<8;i--,k++){if(*(*(chess+i)+k)!=0) //右上⽅{flag4=1;break;}}for(i=row,k=j;i<8&&k>=0;i++,k--){if(*(*(chess+i)+k)!=0) //左下⽅{flag5=1;break;}}if(flag1||flag2||flag3||flag4||flag5){return0;//如果有⼀个位置被占有危险}else return1;} /*int noDanger(int row,int j,int (*chess)[8]){int flag1=0,flag2=0,flag3=0,flag4=0,flag5=0;int i,k;//判断列for(i=0;i<8;i++){if(*(*(chess+i)+j)!=0){flag1=1;break;}}//判断左上⽅for(i=row,k=j;i>=0&&k>=0;i--,k--){if(*(*(chess+i)+k)!=0){flag2=1;break;}}//判断右下⽅for(i=row,k=j;i<8&&k<8;i++,k++){if(*(*(chess+i)+k)!=0){flag3=1;break;}}//判断左下⽅for(i=row,k=j;i<8&&k>=0;k--,i++){if(*(*(chess+i)+k)!=0){flag4=1;break;}}//判断右上⽅for(i=row,k=j;i>=0&&k<8;k++,i--){if(*(*(chess+i)+k)!=0){flag5=1;break;}}if(flag1||flag2||flag3||flag4||flag5){return 0;}else{return 1;}} */EightQueen(int row,int n,int (*chess)[8]){int chess2[8][8];int i,j;for(i=0;i<8;i++){for(j=0;j<8;j++){chess2[i][j]=chess[i][j];}}if(8==row){printf("第%d 种\n",count+1);for(i=0;i<8;i++){for(j=0;j<8;j++)printf("%3d ",*(*(chess2+i)+j));printf("\n");}count++;}else{//判断这个位置是否危险 j<列for(j=0;j<n;j++){if(NotDanger(row,j,chess2))//尝试每⼀列是否危险 {for(i=0;i<8;i++){//整⾏所有列的位置赋值为0*(*(chess2+row)+i)= 0;}*(*(chess2+row)+j)=1;//皇后的位置赋值为1EightQueen(row+1,n,chess2);//继续往下⼀⾏递归 }}}}int main(){int chess[8][8],i,j;for(i=0;i<8;i++){for(j=0;j<8;j++)chess[i][j]=0;}EightQueen(0,8,chess);printf("总共有%d种解决⽅法",count);return0;}。

八皇后回溯算法的实现

八皇后回溯算法的实现

回溯算法的实现(1)为解决这个问题,我们把棋盘的横坐标定为i,纵坐标定为j,i和j的取值范围是从1到8。

当某个皇后占了位置(i,j)时,在这个位置的垂直方向、水平方向和斜线方向都不能再放其它皇后了。

用语句实现,可定义如下三个整型数组:a[8],b[15],c[24]。

其中:a[j-1]=1 第j列上无皇后a[j-1]=0 第j列上有皇后b[i+j-2]=1 (i,j)的对角线(左上至右下)无皇后b[i+j-2]=0 (i,j)的对角线(左上至右下)有皇后c[i-j+7]=1 (i,j)的对角线(右上至左下)无皇后c[i-j+7]=0 (i,j)的对角线(右上至左下)有皇后(2)为第i个皇后选择位置的算法如下:for(j=1;j<=8;j++) /*第i个皇后在第j行*/if ((i,j)位置为空))/*即相应的三个数组的对应元素值为1*/{占用位置(i,j)/*置相应的三个数组对应的元素值为0*/if i<8为i+1个皇后选择合适的位置;else 输出一个解}#include <graphics.h>#include <stdlib.h>#include <stdio.h>#include <dos.h>char n[3]={'0','0'};/*用于记录第几组解*/int a[8],b[15],c[24],i;int h[8]={127,177,227,277,327,377,427,477};/*每个皇后的行坐标*/int l[8]={252,217,182,147,112,77,42,7};/*每个皇后的列坐标*/void *arrow;void try(int i){int j;for (j=1;j<=8;j++)if (a[j-1]+b[i+j-2]+c[i-j+7]==3) /*如果第i列第j行为空*/{a[j-1]=0;b[i+j-2]=0;c[i-j+7]=0;/*占用第i列第j行*/putimage(h[i-1],l[j-1],arrow,COPY_PUT);/*显示皇后图形*/delay(500);/*延时*/if(i<8) try(i+1);else /*输出一组解*/{n[1]++;if (n[1]>'9') {n[0]++;n[1]='0';}bar(260,300,390,340);/*显示第n组解*/outtextxy(275,300,n);delay(3000);}a[j-1]=1;b[i+j-2]=1;c[i-j+7]=1;putimage(h[i-1],l[j-1],arrow,XOR_PUT);/*消去皇后,继续寻找下一组解*/ delay(500);}}int main(void){int gdrive=DETECT,gmode,errorcode;unsigned int size;inITgraph(&gdrive,&gmode,"");errorcode=graphresult();if (errorcode!=grOk){printf("Graphics error\n");exIT(1);}rectangle(50,5,100,40);rectangle(60,25,90,33);/*画皇冠*/line(60,28,90,28);line(60,25,55,15);line(55,15,68,25);line(68,25,68,10);line(68,10,75,25);line(75,25,82,10);line(82,10,82,25);line(82,25,95,15);line(95,15,90,25);size=imagesize(52,7,98,38); arrow=malloc(size);getimage(52,7,98,38,arrow);/*把皇冠保存到缓冲区*/clearviewport();settextstyle(TRIPLEX_FONT, HORIZ_DIR, 4);setusercharsize(3, 1, 1, 1);setfillstyle(1,4);for (i=0;i<=7;i++) a[i]=1;for (i=0;i<=14;i++) b[i]=1;for (i=0;i<=23;i++) c[i]=1;for (i=0;i<=8;i++) line(125,i*35+5,525,i*35+5);/*画棋盘*/for (i=0;i<=8;i++) line(125+i*50,5,125+i*50,285);try(1);/*调用递归函数*/delay(3000);closegraph();free(arrow);}。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一步一步写算法(之八皇后)
【声明:版权所有,欢迎转载,请勿用于商业用途。

联系信箱:feixiaoxing@】
八皇后是一道很具典型性的题目。

它的基本要求是这样的:在一个8*8的矩阵上面放置8个物体,一个矩阵点只允许放置一个物体,任意两个点不能在一行上,也不能在一列上,不能在一条左斜线上,当然也不能在一条右斜线上。

初看到这道题目,大家的第一印象是遍历,但是经过实践之后发现遍历其实不好写,而且复杂度很低。

不仅需要遍历8*8*8*8*8*8*8*8*8 = 2^24次数据,还要判断各种条件,实际的计算复杂度还要比较这个高。

其实我们仔细看一看,这中间很多的计算其实很多是不需要的,因为如果我们在某一行没有可以插入的数据的话,那么这后面的行其实就不用考虑了。

也就是说,我们只有在保证前面插入的物体都合法有效的情况下,才能进行下一次的物体插入。

无谓的遍历只会是无用功。

那么,我们应该怎么做呢?其实步骤不太难:
(1)在第n行寻找可以插入的位置,中间涉及到位置合法性的判断
(2)如果没有可以插入的位置,返回
(3)如果有可以插入的位置,插入数据。

此时再判断是否已经是最后一行,如果是,打印输出返回;反之继续对下一行数据进行试探处理。

有了上面的步骤,我们就可以书写代码了。

老规矩,朋友们可以自己先尝试一下。

a)定义全局堆栈和打印函数
b)添加位置合法性的函数判断
c)八皇后遍历
总结:
(1)迭代递归是编程的难点,需要自己好好实践,看别人写一百遍,不如自己写一遍(2)递归的时候务必注意函数return的出口
(3)递归函数中语句的顺序不要随意更换
(4)递归函数中注意数据的保存和恢复
(5)递归函数也要验证,可以用程序验证法,也可以用其他函数的结果来验证。

相关文档
最新文档