N皇后问题详解

N皇后问题详解

​ N皇后问题 问题实在八皇后问题上的进一步扩展,对于八皇后问题做简单描述。八皇后问题,一个古老而著名的问题,是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有 76 种方案。1854 年在柏林的象棋杂志上不同的作者发表了 40 种不同的解,后来有人用图论的方法解出 92 种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。计算机发明后,有多种计算机语言可以编程解决此问题。

在这里插入图片描述

八皇后问题如果用穷举法需要尝试 88=16,777,216 种情况。每一列放一个皇后,可以放在第 1 行,第 2 行,……,直到第 8 行。穷举的时候从所有皇后都放在第 1 行的方案开始,检验皇后之间是否会相互攻击。如果会,把列 H 的皇后挪一格,验证下一个方案。移到底了就 “进位” 到列 G 的皇后挪一格,列 H 的皇后重新试过全部的 8 行。如图 1 所示,这种方法无疑是非常低效率的,因为它并不是哪里有冲突就调整哪里,而是盲目地按既定顺序枚举所有的可能方案。

回溯算法优于穷举法。将列 A 的皇后放在第一行以后,列 B 的皇后放在第一行已经发生冲突。这时候不必继续放列 C 的皇后,而是调整列 B 的皇后到第二行,继续冲突放第三行,不冲突了才开始进入列 C。如此可依次放下列 A 至 E 的皇后,如图 2 所示。将每个皇后往右边横向、斜向攻击的点位用叉标记,发现列 F 的皇后无处安身。这时回溯到列 E 的皇后,将其位置由第 4 行调整为第 8 行,进入列 F,发现皇后依然无处安身,再次回溯列 E。此时列 E 已经枚举完所有情况,回溯至列 D,将其由第 2 行移至第 7 行,再进入列 E 继续。按此算法流程最终找到如图 3 所示的解,成功在棋盘里放下了 8 个 “和平共处” 的皇后。继续找完全部的解共 92 个。

回溯算法求解八皇后问题的原则是:有冲突解决冲突,没有冲突往前走,无路可走往回退,走到最后是答案。为了加快有无冲突的判断速度,可以给每行和两个方向的每条对角线是否有皇后占据建立标志数组。放下一个新皇后做标志,回溯时挪动一个旧皇后清除标志。八皇后问题进一步扩展即可得到N皇后问题的答案。以下给出回溯算法解八皇后问题的编程代码实现。

N皇后代码实现(递归版本):
#include<stdio.h>
#include<time.h>
#define N 8 //表示皇后个数,以八皇后为例验证结果 
int col[N]={0};
int left[2*N-1]={0};
int right[2*N-1]={0};
int hong[N]={0};
int n=1;
void printqueen()
{
	int x,y;
//	for(i=0;i<8;i++)
//		printf("%d ",hong[i]);
  printf("输出第%d个皇后\n",n++);
 for(x=0;x<N;x++)
   {
	   for(y=0;y<N;y++)
	   {
		   if(hong[x]==y)
             printf("1");
		   else
			 printf("0");
       }
	   printf("\n");
   }
}
void queen (int i)
{
	int j;
	for(j=0;j<N;j++)
	{
		if(col[j]==0&&left[i+j]==0&&right[i-j+7]==0)
		{
			hong[i]=j;col[j]=1;right[i-j+7]=1;left[i+j]=1;
		  if(i<N-1)
			  queen(i+1);
		  else
		  {
			printqueen();
		//	n++;
		  }
		  col[j]=right[i-j+7]=left[i+j]=0;
		}
	}
}
int main(void)
{
	int a,b;
//	freopen("main.txt","w",stdout);
	a=clock();
    queen(0);
    b=clock();
 printf("%d  %d\n",n-1,b-a);
   return 0;
}

程序输出结果:

在这里插入图片描述

N皇后代码实现(非递归版本):
#include<stdio.h>
#include<time.h>
#define N 8  //表示皇后个数,以八皇后为例验证结果 
int col[N]={0};//标记列是否存放皇后
int right[2*N-1]={0};//标记左斜线是否放皇后
int left[2*N-1]={0};//标记右斜线是否放皇后
int q[N]={-1};//机录皇后位置
int cnt=0;
//***************************************************
// 初始化数组
//清空上次数据记录
//***************************************************
void init()
{
	int i;
	for(i=0;i<2*N-1;i++)
	{

		right[i]=left[i]=0;
		if(i<N)
		{
			col[i]=0;
			q[i]=-1;
		}
	}
}
//***************************************
//输出函数
//打印皇后坐标 以0,1格式输出
//***************************************

void printqueen()
{
	int i,j;
	printf("\n\n");
	printf("输出第%d个皇后\n",++cnt);
	for(i=0;i<N;i++)
	{
		for(j=0;j<N;j++)
		{
			if(q[i]==j)//有皇后则输出唯1
			printf("1");
			else 
			printf("0");
		}
		printf("\n");
	}
}
//*****************************************************
// 功能函数
// 判断皇后位置并输出每种皇后结果
//*****************************************************

void Tiral()
{
	int i,j,top=0;
	for(i=1;i<N&&i>0;i++)//继续主调循环从第1行开始遍历求解
	{
		j=q[top+1]+1;
		q[top+1]=-1;
		for(;j<N;j++)//查找每行每个空格是否符合条件
		{
			if(!col[j]&&!left[i+j]&&!right[i-j+7])
			{
				col[j]=left[i+j]=right[i-j+7]=1;
				q[++top]=j;//记录皇后位置
				break;//如果放完皇后跳出内层循环
			}
        }
		if(j==N)//如果j等于N摸掉皇后回朔回前一行
		{
		    top--;
			col[q[top+1]]=left[top+1+q[top+1]]=right[top+1-(q[top+1])+7]=0;//摸掉top以行皇后
			i=i-2;
		}
		if(j<N)
		{
			if(i==N-1)
			{
				printqueen();
			   	top--;
		        col[q[top+1]]=left[top+1+q[top+1]]=right[top+1-(q[top+1])+7]=0;//抹掉的N-1皇后
				i--;
			}
		}
		}
}                                                                                               
int main(void)
{
	int i,j,a,b;
//	freopen("main.txt","w",stdout);
	a=clock();//记录开始时间
	for(i=0;i<N;i++)//循环初始化0行数据
	{
        init();//初始化标记数组
		q[0]=i;	//初始化0行
		col[i]=left[i]=right[7-i]=1;//标记数组初始化
    	Tiral();//从第一行开始进行回朔穷举找结果并输出
	}
	b=clock();//记录结束时间
	printf("%d %d",cnt,(b-a));//输皇后数量,计算时间
	return 0;
}

程序输出结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泸州月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值