回溯法——八皇后问题【通俗易懂】
因为最近在学习算法,所以今天在这里对回溯法中的八皇后问题,进行一下归纳和总结,真的是用不能再通俗的语言去解释了,看不懂请自绝与人民。
一、基本定义
回溯法(back track method)是在包含问题的所有可能解的解空间树中,从根结点出发,按照深度优先的策略进行搜索,对于解空间树的某个结点,若满足约束条件,则进入该子树继续搜索,否则将以该结点为根结点的子树进行剪枝。
二、适用范围
可避免搜索所有的可能解,适用于求解组合数较大的问题。
三、八皇后问题
问题:在8 x 8的棋盘上摆放8个皇后,而且八个皇后中的任意两个是不能处于同一行、同一列、或同一斜线上。
【分析】
在8 x 8的棋盘上面放置8个皇后,而且还要不在不同一行和不在同一列,不在同一斜线上,所以每行肯定是得放一个,但是位置就有好多的可能,只要满足上面的要求即可。
设棋盘是一个8 x 8矩阵,皇后i和皇后j的摆放位置分别为(i,Xi)和(j,Xj),要想这些皇后不在同一条斜线上,则需要这两个坐标点的斜率不等于 1 或 – 1。
也就是满足|Xj —Xi | ≠ |j – i|
这里采用迭代法解决八皇后问题,迭代就是循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值。
迭代的代表性例子:实数累加
int v=1;
for(i=2;i<=100;i++)
{
v=v+i; //1 ——100的累加
}
【具体步骤】
1、这里的八个皇后用k = 0,1,2,3,4,5,6,7来表示。
2、第一个皇后放在8 x 8矩阵的(0 , 0)位置,也就是k = 0 ,x[k] = 0 ,这里的k表示行和皇后k,x[k]表示列。
3、放完第一个皇后之后,就要放置第二个皇后,因为不能在同一行,所以第二个皇后肯定在第二行放,这个时候到底在哪一列还没有确定。
4、在确定第二个皇后到底在哪一列的时候,就要用到一个判断函数,这个函数主要是确定皇后不在同一列和同一斜线上。
check(int k),检查皇后k是否会发生冲突。
int check(int k){ //查看k皇后是否满足约束条件
for(int i=0;i<k;i++)
if (x[i]==x[k] || abs(x[i]-x[k]) ==abs(i-k)) //满足不在同一条斜线和同一列
return 1;
return 0;
}
5、若发生了冲突【即皇后在同一列或同一斜线上】,就x[k]++,一直找到不发生冲突的位置或越界。
6、在找到皇后的位置之后,要先判断一下是否皇后都找到了合适的位置。
7、若还有剩余的皇后或发生越界,则分情况处理,若是还有剩余的皇后,则k = k+1,摆放下一个皇后,若是发生越界,则说明需要回溯,则x[k] = -1 ,k = k – 1
八皇后问题C++代码:
#include<iostream>
using namespace std;
#include<stdlib.h>
static int *x; //用x数组来存放解向量
static int sum; //用sum变量来记录有几个解
int check(int k){ //查看k皇后是否满足约束条件
for(int i=0;i<k;i++)
if (x[i]==x[k] || abs(x[i]-x[k]) ==abs(i-k)) //满足不在同一条斜线和同一列
return 1;
return 0;
}
void queen(int n){
int k = 0; //从 皇后0 开始放
sum = 0;
while(k>=0){
x[k]++; //摆放第k个皇后(第一次摆放皇后0)
while(x[k]<n && check(k) == 1) //对皇后k进行检测,直到不发生冲突或x[k]越界
x[k]++; //检测下一列
if(x[k]<n && k == n-1)
{
for(int i =0;i<n;i++)
cout<<x[i]+1<<" ";
cout<<endl;
sum++;
}
if(x[k]<n && k<n-1) //若皇后还没有摆放完,就摆放下一个皇后k = k+1
k++;
else //否则就是发生了越界,要进行回溯
{
x[k]=-1;
k--;
}
}
if(sum == 0)
cout<<"无解"<<endl;
}
int main(){
int n =8;
x = new int[n+1];
for(int i=0;i<n;i++){
x[i] = -1;
}
queen(n);
cout<<"一共解的个数为 :"<<sum<<endl;
return 0;
}