八皇后问题详细的解法[优质ppt]
- 格式:ppt
- 大小:1.16 MB
- 文档页数:24
算法——⼋皇后问题(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&s;8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法挺直吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。
我们定义这样的一个一维数组,数组的下标表示第i行,数组的值表示第i列,数组大小为8,把数组初始化为从0到7的八个不同值。
相当与对01234567做全罗列,这样所以的数字自然不再同一行,同一列,即数组下标的值各不相同,数组的值各不相同,且都是取从0到7的不同值。
现在要解决的就是对角线的问题,假如两个元素处在同一对角线,则有数组下标之差的肯定值等于数组元素值之差的肯定值。
即 abs(i-j) == abs(array[i] - array[j])。
代码如下: ilude stdio.h include stdlib.h include math.h ic int total = 0;const int QueensNum = 8;vo swap(int * a, int * b){ if(a == b) return;*a = *a + *b; *b = *a - *b; *a = *a - *b;}int check(int array[], int n){ int i, j; for(i=0;i n;++i) { for(j=i+1;j n;++j)if(abs(i-j) == abs(array[i] - array[j])) return 0; } return 1;}void peutation(int array[], int ind, int n){ int i; if (index == n) { if(check(array, n)) { for(i = 0; i n; ++i){ printf(\"(%d,%d) \",i,array[i]); } printf(\"n\"); ++total; } } ee { for (i = index; i n; i++) { swap( array[index], array[i]); permutation(array, index + 1, n); swap( array[index],array[i]); } }}void QueensInit(int array[],int n){ int i; for(i = 0; i n; ++i) { array[i] = i; }}int main(int argc, char**argv){ int array[QueensNum]; QueensInit(array, QueensNum); permutation(array, 0 ,QueensNum); printf(\"total solution number is %dn\", total);第1页共1页。
递归算法——八皇后问题1、背景问题描述八皇后问题是一个古老而著名的问题,该问题的目标是在8×8的国际象棋棋盘上放置八个皇后,使得任意两个皇后都不在同一行,或同一列或同一对角线上。
如图1所示就是八皇后问题的一个解。
图1 八皇后问题的一个解1854年在柏林的象棋杂志上不同的作者发表了40种不同的解。
大数学家高斯认为有76种不同的方案。
后来有人用图论的方法算出92种不同的解。
能否利用程序算出所有满足条件的解的数目呢?2、抽象表示及存储对于这种矩形排列的棋盘而言,用二维数组存储棋盘的状况是最容易想到的方法。
可以设置一个8*8的二维数组,令有棋子的位置为1,无棋子的部分为0。
事实上,由于8个皇后中任意两个皇后都不在同一行,因此8个皇后只能各自占据一行。
不妨认为8个皇后编号为0、1、……、7,它们各自占据棋盘的第1行、第2行、……、第8行。
从而可以使用长度为8一维数组表示棋盘状态,数组元素的下标表示棋子所在行,数组元素的值表示各个棋子所在的列。
使用的存储方式不同,其采用的算法已有很大区别。
3、问题分析及算法设计假定用二维数组A存储棋盘的情况,可以考虑下面两种思路。
思路一:不考虑任何限制的穷举法。
用8×8的二维数组存储棋盘,若在(i,j)处有子,则令A[i][j]=1,否则A[i][j]=0。
于是8个棋子第1个有64种摆放方法,第2个有63种放法,……,第8个有57种放法,则所有摆放方法有64×63×62×…×57种。
可以列举每一种摆法,而后考察每种方法是否符合条件。
这个计算量非常大。
思路二:考虑经过优化的穷举法(二维数组方案)。
若8个棋子位于8行8列的棋盘中,要求任意两个不同行、不同列,则任一解必然是各行、各列只包含一个棋子,其它情况必然不是解。
于是可以做个8重循环,把每个皇后安排在每行的每个位置都试一遍。
算法如下:将整个棋盘数组赋值为0;for(1号皇后从1行1列到1行8列){将1号皇后能控制的线路(横向、竖线、斜线)全部设为1;for(2号皇后从2行1列到2行8列){if(2号皇后控制的线路全部为0){将2号皇后能控制的线路(横向、竖线、斜线)全部设为2;for(3号皇后从3行1列到3行8列){if(3号皇后控制的线路全部为0){将3号皇后能控制的线路全部设为3;……for(8号皇后从8行1列到8行8列){if(8号皇后控制的线路全部为0){将8号皇后能控制的线路全部设为8;记录该棋盘为一个解;}将8号皇后控制的线路全部恢复为0;}……}将3号皇后控制的线路全部恢复为0;}}将2号皇后控制的线路全部恢复为0;}将1号皇后控制的线路全部恢复为0}上述算法中的多重循环虽易于理解,但程序嵌套结构较为复杂,形式死板,不易扩展。
内容摘要八皇后问题是十九世纪著名数学家高斯于1850年提出的。
问题是:在8*8的棋盘上摆放8个皇后,使其不能互相攻击,即任意的两个皇后不能处在同意行,同一列,或同意斜线上。
可以把八皇后问题拓展为n皇后问题,即在n*n的棋盘上摆放n个皇后,使其任意两个皇后都不能处于同一行、同一列或同一斜线上。
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。
但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为"回溯点"。
关键词:八皇后问题;回溯法;探索;选优;试探法;目录1.引言 (1)1.1研究的缘起 (1)1.2本文的研究思路、方法及意义 (1)1.3相关理论基础 (1)2.实验过程分析 (1)2.1算法描述 (1)2.2实验工具 (1)3.结果与讨论 (1)3.1算法分析 (1)3.2研究与结论 (3)4.设计体会 (5)5.参考文献 (5)1.引言1.1研究的缘起在8X8格的国际象棋棋盘上放置8个皇后,使得任意两个皇后不能互相攻击,即任何行、列或对角线(与水平轴夹角为45°或135°的斜线)上不得有两个或两个以上的皇后。
这样的一个格局称为问题的一个解。
请用回溯算法写出求皇后问题的算法。
1.2本文的研究思路、方法及意义每一行可以而且必须放一个皇后,所以n皇后问题的解可以用一个n元向量X=(x1,x2,.....xn)表示,其中,1≤i≤n且1≤xi≤n,即第n个皇后放在第i行第xi列上。
由于两个皇后不能放在同一列上,所以,解向量X必须满足的约束条件为:xi≠xj;若两个皇后的摆放位置分别是(i,xi)和(j,xj),在棋盘上斜率为-1的斜线上,满足条件:i-j=xi-xj;在棋盘上斜率为1的斜线上,满足条件:i+j=xi+xj;综合两种情况,由于两个皇后不能位于同一斜线上,所以,解向量X必须满足的约束条件为:|i-xi|≠|j-xj|;显然,八皇后问题就可以根据解n皇后的这个思路去解决。
八皇后问题(递归+非递归)Xredman posted @ 2009年6月04日 21:15 in 以前博文 , 442 阅读一.问题描述在8×8格的国际象棋棋盘上放置八个皇后,使得任意两个皇后不能互相攻击,即任何行、列或对角线(与水平轴夹角为45°或135°的斜线)上不得有两个或两个以上的皇后。
这样的一个格局称为问题的一个解。
请用递归与非递归两种方法写出求出八皇后问题的算法。
二.解题思路描述一个正确的解应当是每一列,每一行,每一条斜线上均只有一个皇后。
对于递归算法,本人才有模拟的方式进行,而且,我觉得开辟一个二维数组更显而易见。
首先,从空棋盘开始摆放,保证第m行m个皇后互不攻击,然后摆放第m+1个皇后。
当然对于第m+1个皇后可能有多种摆放方法,由此,我必须一一枚举,采用回溯策略是可行且合乎逻辑的。
而对于非递归算法,我只是借助于书本上一个递归改为非递归的框架,依次搭建而已。
在此过程中,我采用一维数组,一位对于八皇后问题,每一行不可能存在二个及二个以上的皇后,board[i]表示第i行棋盘摆放的位置为第board[i]列。
递归方法借助于系统提供的栈,而我非递归算法的实现,仅仅是自己构造一个栈而已。
递归解法#include <iostream>#include <cstdio>#include <sys/timeb.h>using namespace std;const int MAX_SIZE = 100;enum flag {blank ='X',queen = 1};char Chess[MAX_SIZE][MAX_SIZE];//棋盘图int n;//解决n皇后问题int total;//用于计摆放方式void Init(){//对棋牌进行初始化for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)Chess[i][j] = blank;total = 0;//初始时有零中摆放方式}bool Judge(int r,int c){//判断(r,c)位置是否可放置int i,j;for(i = r + 1; i < n; i++)if(Chess[i][c] == queen)return false;//说明c列上已有一皇后for(i = c + 1; i < n; i++)if(Chess[r][i] == queen)return false;//说明r行上已有一皇后for(i = r + 1, j = c + 1; (i < n) && (j < n); i++, j++)if(Chess[i][j] == queen)return false;//45度斜线上已有一皇后for(i = r + 1, j = c - 1; (i <n) && (j >= 0); i++, j--)if(Chess[i][j] == queen)return false;//135度斜线上已有一皇后return true;//排除四种情况后,说明(r,c)点可放置皇后}void Backtrack(int k,int cnt){//回溯算法主程序if(k < 0 || cnt == n)//棋牌摆放完毕 or 以摆满n后{if(cnt == n){printf("No.%d:\n",++total);for(int i = 0; i < n; i++){for(int j = 0; j < n; j++)printf(" %c ",Chess[i][j]);putchar('\n');}putchar('\n');}}else{int r = k / n, c = k % n;if(Judge(r,c)){//可放置一皇后Chess[r][c] = queen;Backtrack(k-1,cnt+1);Chess[r][c] = blank;}Backtrack(k-1,cnt);}}int main(){//此为主函数timeb t1,t2;long kk;cout<<"输入皇后个数:";while(cin>>n){Init();ftime(&t1);Backtrack(n*n-1,0);ftime(&t2);cout<<"计算"<<n<<"后问题总共可有"<<total<<"种摆法!"<<endl;kk = (t2.time-t1.time)*1000 +litm;cout<<"本次回溯耗时:"<<kk<<"毫秒"<<endl;system("PAUSE");cout<<"输入皇后个数:";}return0;}非递归解法#include <iostream>#include <sys/timeb.h>#define N 100using namespace std;int board[N];int n,sum;void init(){for(int i = 1; i <= n; i++)board[i] = 0;}void display(){int i,j;cout<<"No."<<sum<<endl;for(i = 1; i <= n; i++){for(j = 1; j <= n; j++)if(board[i] == j)cout<<"Q ";elsecout<<"X ";cout<<endl;}cout<<endl;}bool canPut(int k){for(int i = 1; i < k; i++)if((abs(k - i) == abs(board[k] - board[i])) || board[i] == board[k])return false;//1.是否在同一斜线;2.是否位于同一列return true;}void Backtrack(){board[1] = 0;int k = 1;while(k > 0){board[k]++;while((board[k] <= n) && !(canPut(k)))board[k] += 1;if(board[k] <= n)if(k == n){sum++;display();}else{k++;board[k] = 0;}elsek--;}}int main(){timeb t1,t2;long kk;cout<<"输入皇后个数:";while(cin>>n){init();sum = 0;ftime(&t1);Backtrack();ftime(&t2);cout<<"总共排列方式为:"<<sum<<endl;kk = (t2.time-t1.time)*1000 + litm; cout<<"本次回溯耗时:"<<kk<<"毫秒"<<endl;system("PAUSE");cout<<"输入皇后个数:";}return0;}。