八皇后(递归+回溯)
- 格式:doc
- 大小:341.00 KB
- 文档页数:3
⼋皇后问题(经典算法-回溯法)问题描述:⼋皇后问题(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;}。
回溯算法原理和几个常用的算法实例回溯算法是一种基于深度优先的算法,用于解决在一组可能的解中找到满足特定条件的解的问题。
其核心思想是按照特定的顺序逐步构造解空间,并通过剪枝策略来避免不必要的。
回溯算法的实现通常通过递归函数来进行,每次递归都尝试一种可能的选择,并在达到目标条件或无法继续时进行回溯。
下面介绍几个常用的回溯算法实例:1.八皇后问题:八皇后问题是一个经典的回溯问题,要求在一个8×8的棋盘上放置8个皇后,使得每个皇后都不能相互攻击。
即每行、每列和对角线上都不能有两个皇后。
通过在每一列中逐行选择合适的位置,并进行剪枝,可以找到所有满足条件的解。
2.0-1背包问题:0-1背包问题是一个经典的组合优化问题,要求在一组物品中选择一些物品放入背包,使得其总重量不超过背包容量,同时价值最大化。
该问题可以通过回溯算法进行求解,每次选择放入或不放入当前物品,并根据剩余物品和背包容量进行递归。
3.数独问题:数独问题是一个经典的逻辑推理问题,要求在一个9×9的网格中填入数字1-9,使得每行、每列和每个3×3的子网格中都没有重复数字。
该问题可以通过回溯算法进行求解,每次选择一个空格,并依次尝试1-9的数字,然后递归地进行。
4.字符串的全排列:给定一个字符串,要求输出其所有可能的排列。
例如,对于字符串"abc",其所有可能的排列为"abc"、"acb"、"bac"、"bca"、"cab"和"cba"。
可以通过回溯算法进行求解,每次选择一个字符,并递归地求解剩余字符的全排列。
回溯算法的时间复杂度通常比较高,因为其需要遍历所有可能的解空间。
但是通过合理的剪枝策略,可以减少的次数,提高算法效率。
在实际应用中,可以根据具体问题的特点来设计合适的剪枝策略,从而降低算法的时间复杂度。
⼋皇后问题(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表⽰右斜线;⾸先从最下⽅开始,所以只需考虑下⾯的⾏。
八皇后问题最简单算法
八皇后问题最简单算法是使用回溯法。
1. 回溯法在求解八皇后问题时,会生成一个8位的二进制数,每一位代表一列是否放置皇后。
如果某一列放置了皇后,则该位为1,否则为0。
2. 在放置皇后时,如果当前位置可以放置皇后,则尝试放置。
如果放置后当前位置形成了一个合法的棋盘,则继续递归地放置下一个皇后。
如果放置后形成了一个不合法的棋盘,则回溯到上一个状态,尝试其他位置。
3. 如果所有8个皇后都放置完毕,且形成了一个合法的棋盘,则找到了一个解。
以上信息仅供参考,如需了解更多信息,建议查阅八皇后问题相关书籍或咨询专业人士。
回溯算法原理和几个常用的算法实例回溯算法是一种通过不断尝试和回退的方式来进行问题求解的算法。
它的基本思想是在过程中,当发现当前的选择并不符合要求时,就进行回退,尝试其他的选择,直到找到符合要求的解或者遍历完所有可能的选择。
回溯算法通常用于问题求解中的和排列组合问题,比如求解八皇后问题、0-1背包问题、数独等。
下面将介绍几个常用的回溯算法实例。
1.八皇后问题:八皇后问题是指在一个8×8的国际象棋棋盘上,放置八个皇后,使得任意两个皇后都不在同一行、同一列或同一斜线上。
可以通过递归的方式依次尝试每一行的位置,并判断当前位置是否满足条件。
如果满足条件,则进入下一行尝试;否则回溯到上一行,并尝试其他的位置,直到找到解或遍历完所有的可能。
2.0-1背包问题:0-1背包问题是指在给定一组物品和一个容量为C的背包,每个物品都有自己的重量和价值,求解在不超过背包容量时,如何选择物品使得背包中物品的总价值最大。
可以通过递归的方式依次考察每个物品,并判断是否选择当前物品放入背包。
如果放入当前物品,则背包容量减小,继续递归考察下一个物品;如果不放入当前物品,则直接递归考察下一个物品。
直到遍历完所有物品或背包容量为0时,返回当前总价值。
3.数独问题:数独是一种通过填充数字的方式使得每一行、每一列和每一个九宫格内的数字都满足一定条件的谜题。
可以通过递归的方式依次尝试填充每一个空格,并判断当前填充是否符合条件。
如果符合条件,则继续递归填充下一个空格;如果不符合条件,则回溯到上一个空格,并尝试其他的数字,直到找到解或遍历完所有的可能。
回溯算法的时间复杂度一般较高,通常为指数级别。
因此,在实际应用中,可以结合剪枝等优化策略来提高算法的效率。
此外,回溯算法也可以通过非递归的方式进行实现,使用栈来存储当前的状态,从而避免递归带来的额外开销。
总之,回溯算法是一种非常有效的问题求解方法,通过不断尝试和回退,可以在复杂的空间中找到符合要求的解。
八皇后问题(递归+非递归)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;}。
八皇后问题的设计思路
八皇后问题是一个经典的回溯算法问题,它的设计思路主要包括以下几个步骤:
1. 问题描述:明确问题要求,即在8x8的国际象棋棋盘上放置8个皇后,使得它们彼此之间不互相攻击,也就是不在同一行、列或斜线上。
2. 设计算法:对于八皇后问题,我们可以采用回溯法来解决。
回溯法的基本思想是从一条路走到底,如果发现走不通,就回退到上一步,尝试走其他路。
回溯法需要一个递归的思路,从最大的局面开始,逐步缩小局面,直到找到符合条件的解。
3. 求解思路:从棋盘的第一行开始,依次判断当前位置是否可以放置皇后。
判断的依据是:如果当前位置所在的列已经有皇后,或者在两条斜线上与上方的皇后在同一行或列,那么当前位置就不符合要求。
如果当前位置符合要求,就继续尝试下一列。
如果尝试到最后一行,所有皇后都摆放完毕,那么就找到了一个解。
4. 算法实现:可以使用递归或迭代的方式实现回溯算法。
递归实现时,需要定义一个函数,该函数接受当前局面(即已经摆放好
的皇后和当前位置)作为参数,如果当前位置符合要求,就继续递归调用该函数,直到找到所有解。
迭代实现时,需要定义一个循环,每次循环尝试一个位置,如果当前位置符合要求,就继续尝试下一列,直到找到所有解。
5. 结果输出:当找到所有解后,需要将它们输出。
可以定义一个数组或列表来存储所有解,每找到一个解,就将其添加到列表中。
输出时,可以按照列表的顺序输出所有解。
总的来说,八皇后问题的设计思路主要包括明确问题描述、设计算法、求解思路、算法实现和结果输出几个步骤。
通过回溯法,我们可以找到所有符合条件的解,并将其输出。
实验题目回溯法实现8皇后问题实验要求 a.掌握递归回溯算法的基本思想。
b.学习掌握应用面向对象通用回溯程序框架解决实际问题。
提高面向对象编程的技能。
实验内容(问题描述、算法设计、算法效率)回溯法实现8皇后问题a.问题描述:在n*n格的棋盘上放置彼此不受攻击的n个皇后。
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
N后问题等价于在n*n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。
b.算法设计:用n元组x[1;n]表示n后问题的解。
其中,x[i]表示皇后i放置在棋盘的第i行的第x[i]列。
由于不容许将2个皇后放在同一列上,所以解向量中的x[i]互不相同。
2个皇后不能放在同一斜线上是问题的隐约束。
对于一般的n后问题,这一隐约束条件可以化成显约束的形式。
如果将n*n 格的棋盘看做二维方阵,其行号从上到下,列号从左到右依次编号为1,2,...n。
从棋盘左上角到右下角的主对角线及其平行线(即斜率为-1的各斜线)上,2个下标值的差(行号-列号)值相等。
同理,斜率为+1的每条斜线上,2个下标值的和(行号+列号)值相等。
因此,若2个皇后放置的位置分别是(i,j)和(k,l),且i-j = k -l 或i+j = k+l,则说明这2个皇后处于同一斜线上。
以上2个方程分别等价于i-k = j-l 和i-k =l-j。
由此可知,只要|i-k|=|l-j|成立,就表明2个皇后位于同一条斜线上。
用回溯法解n后问题,用完全n叉树表示解空间。
可行性约束Place剪去不满足行,列和斜线约束的子树。
下面的解n后问题的回溯法中,递归函数Backtrack(1)实现对整个解空间的回溯搜索。
Backtrack(i)搜索解空间中第i层子树。
类Queen的数据成员记录解空间中节点信息,以减少传给Backtrack的参数。
sum记录当前已找到的可行方案数。
在算法Backtrack中,当i>n是,算法搜索至叶节点,得到一个新的n皇后互不攻击放置方案,当前已找到的可行方案数sum增1。
使用递归+回溯实现八皇后问题:
程序如下:
// eightequeen.cpp : 定义控制台应用程序的入口点。
//i,k表示行号,x[i]表示第i行对应的列值;
#include"stdafx.h"
#include<math.h>
#include<iostream>
using namespace std;
#define N 8
int sum =0;
int *x = new int[N+1];
bool issafe(int k)//判断是否有冲突;
{
int i;
for(i = 1; i<k;i++)
{
if(x[i]==x[k]||abs(i-k)==abs(x[i]-x[k]))//判断是否在同一列以及是否是对角线上;
return false;
}
return true;
}
void digui(int t)//递归实现八皇后问题;
{
int i = 0;
if(t>N)//如果t大于N,表示排好一个不冲突的布局,将其输出;
{
cout<<"输出不冲突的布局:"<<"";
for(i = 1;i<=N;i++)
cout<<x[i]<<"";
cout<<endl;
sum++;
}
else
{
for(i=1;i<=N;i++)
{
x[t] = i;
if(issafe(t))//如果该行没有冲突,则考虑下一行;
digui(t+1);
}
}
}
void huisu() //回溯法实现八皇后问题
{
int i;
x[1] = 0;
int k = 1;
while (k>0)
{
x[k]+=1;
while((x[k]<=N)&&!issafe(k))//当列小于N并且有冲突时,则搜索下一个位置;
x[k]+=1;
if(x[k]<=N)
{
if(k==N)//最后一个皇后时,则输出一个不冲突的布局;
{
cout<<"输出不冲突的布局:"<<"";
for(i = 1;i<=N;i++)
cout<<x[i]<<"";
cout<<"\n"<<"";
sum++;
}
else//如果不是最后一个皇后,则布置下一个皇后;
{
k++;
x[k]=0;
}
}
else//回溯
{
k--;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"递归实现八皇后问题:\n"<<"";
digui(1);
cout<<"一共有多少个布局:"<<sum<<endl;
cout<<"回溯实现八皇后问题:\n"<<"";
sum = 0;
huisu();
cout<<"一共有多少个布局:"<<sum<<endl;
return 0;
}
程序运行后输出的结果如下:。