从八皇后问题剖析回溯算法

首先让我们来看一下什么是八皇后问题
在8*8的棋盘上放置8个皇后而彼此不受攻击(即在棋盘的任一行,任一列和任一对角线上不能放置2个皇后)
在这里插入图片描述

这道题运用回溯算法,那么什么是回溯算法?

简单来说,就是先按照一种方法向前试探,当发现这种方法不是最优解或者不能到达目标时,退回一步,换下一种方法继续试探,知道找到终点为止

其实回溯算法都有一个很相似的套路

void go(int k)
{
    for(i=1;i<=k;i++)
    {
  	if(符合条件)
  	{
  		 ……
   		if(到达终点) {}
   		else go(k+1);
   		//进行回溯
   		返回一步 …… 
  	} 
    }
} 

那么这个八皇后问题也是同理
比如
在这里插入图片描述先在(1,1)放置第一个皇后
然后进入第二行放置第二个皇后
先进入(2,1),发现与第一个冲突,右移,到达(2,2)发现还与第一个冲突,继续右移,到达(2,3),没有冲突,所以第二个皇后成功占领
在这里插入图片描述

第三个皇后占领的过程同理,先到达(3,1),发现与(1,1)冲突,右移,到达(3,2)发现与(2,3)冲突,右移,到达(3,3)与(2,3)冲突,右移,到达(3,4),与(2,3)冲突,右移,到达(3,5),没有冲突,成功占领

在这里插入图片描述
那么如何体现回溯呢?
就像下图
在这里插入图片描述

我们此时已经填充完前7个皇后了,但是发现第8个皇后无论在哪都会与其他皇后冲突,这时我们就倒退一步,将第7个皇后换一下位置,继续右移,放在(7,7)与第四个皇后冲突,(7,8)与第二个皇后冲突,此时第7个皇后已经没有位置可以选择,那就继续回溯,移动第6个皇后,将他右移,不断寻找合适位置

这就是回溯
那么具体如何处理这个八皇后问题呢

首先我们应该知道,为了避免冲突就应该没有同一行,同一列,对角线上没有两个皇后

在这里插入图片描述由图中易得,在同一右下方对角巷上的元素,行数-列数总是相同

同理可知,左下方向的对角线元素,行数+列数总是相同

之后就可以大致得出关键代码

准备数组queen,表示在第i行的第j列放置元素queen[i]=j;
flag数组表示同一列有无冲突
d1表示右下方向对角线是否冲突,这里+7是为了让所有的负数都变为非负数
if(n<8)就表示8个皇后没有排满,还需要继续递归
否则就打印结果

下面的回溯只有打印结果步骤结束之后在可以运行,此时,需要撤销最后一步,寻求其他结果

//判断位置是否冲突 
if((!flag[col])&&(!d1[n-col+7])&&(!d2[n+col]))
  {
   queen[n]=col;//在第n行放置皇后 
   flag[col]=1;//占领col列 
   d1[n-col+7]=1;//占领两个对角线 
   d2[n+col]=1;
   if(n<8)
    display(n+1);//8个皇后没有摆完,就继续递归 
   else
    print();//n=8说明已经排列完成 
   
   //回溯:考虑其他可行方案 
   flag[col]=0;
   d1[n-col+7]=0;
   d2[n+col]=0;  
  }

下面附上全部代码

#include<iostream>
using namespace std;

int queen[9]={0};//第i个皇后所在列数 
int flag[9]={0,0,0,0,0,0,0,0,0};//表示第i列是否可占 
int d1[17]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//表示“/”对角线是否可占 
int d2[17]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//表示“\”对角线是否可占
int number=0;//种类数 

void print();//打印函数 
void display(int n);//回溯函数 

int main()
{
	display(1);
	return 0;
}
void display(int n)
{
	int col;
	for(col=1;col<=8;col++)//每种皇后都有8种可能 
	{
		if((!flag[col])&&(!d1[n-col+7])&&(!d2[n+col]))//判断位置是否冲突 
		{
			queen[n]=col;//在第n行放置皇后 
			flag[col]=1;//占领col列 
			d1[n-col+7]=1;//占领两个对角线 
			d2[n+col]=1;
			if(n<8)
				display(n+1);//8个皇后没有摆完,就继续递归 
			else
				print();//n=8说明已经排列完成 
			
			//回溯:考虑其他可行方案 
			flag[col]=0;
			d1[n-col+7]=0;
			d2[n+col]=0;		
		}
	}
}
void print()
{
	int col,i,j;
	number++;
	cout<<"No."<<number<<endl;
	int table[9][9]={0};
	for(col=1;col<=8;col++)
	{
		table[col][queen[col]]=1;
	}
	for(i=1;i<=8;i++)
	{
		for(j=1;j<=8;j++)
		{
			cout<<table[i][j]<<" ";
		}
		cout<<endl;
	}
}

运行结果如下,可知8皇后有92种可能

在这里插入图片描述
由此我们知道,回溯算法是一个类似枚举的试探性过程,当发现已不满足求
解条件时,就“回溯”返回,尝试别的路径,能进则进,不能进再退回来

如果这篇文章对你有帮助,记得点赞关注哦

发布了37 篇原创文章 · 获赞 3 · 访问量 1163

猜你喜欢

转载自blog.csdn.net/qq_45721778/article/details/105330358
今日推荐