OpenJudge-2.5基本算法之搜索-1700八皇后问题题解

八皇后问题

题目描述

在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。

输入格式

无输入

输出格式

按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。

样例输出 #1

No. 1
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 1 0 0 0 0 0 
No. 2
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
0 0 1 0 0 0 0 0 
No. 3
1 0 0 0 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 1 
0 0 1 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 1 0 0 0 0 
0 1 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
No. 4
1 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 1 
0 0 0 0 0 1 0 0 
0 0 1 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 1 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 
No. 5
0 0 0 0 0 1 0 0 
1 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
0 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 
0 0 1 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 1 0 0 0 0 
No. 6
0 0 0 1 0 0 0 0 
1 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 1 0 0 0 0 0 
0 0 0 0 0 1 0 0 
No. 7
0 0 0 0 1 0 0 0 
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 
0 0 0 1 0 0 0 0 
0 1 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 1 0 0 0 0 0 
0 0 0 0 0 1 0 0 
No. 8
0 0 1 0 0 0 0 0 
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
No. 9
0 0 0 0 1 0 0 0 
1 0 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 1 0 0 0 0 0 
...以下省略

提示

此题可使用函数递归调用的方法求解。

题目思路

此题我们可以使用枚举的方法求解,但如果枚举一个棋盘,即64个棋子,程序很容易超时,因此我们需要减少枚举层数。
我们可以以一行或者一列为一层来枚举,因为每行每列只能有一个棋子。
那么我们反应该把枚举单位定为一行还是一列呢?
简单观察题目给出的九个样例,不难发现题目是以列作为最小枚举单位的,即先考虑第一列第一行有棋子,再考虑第一列第二行有棋子。
现在程序的基本框架已经确定,是一个八重循环,为了使代码更加简洁,我们将八重循环写成一个递归函数

代码的逐步实现

1.函数的“定类型”

本题函数无需返回任何值,只需要在最后时将枚举的结果输出即可,因此我们定义函数的类型为void
代码实现如下

#include<iostream>
using namespace std;

void fun( )//这里不确定参数定义几个,定义什么类型
{
	
}
int main()
{
	fun( );
	return 0;
}

2.函数的“定参数”和“定类型”

本题函数需要记录当前执行的函数是第几层枚举,也就是程序将从第一层枚举到第八层,然后在第九次执行函数时输出枚举结果。
所以在主函数传参时,传参数n为1,即第一层枚举。
然后当n==9时,输出结果
代码实现如下

#include<iostream>
using namespace std;

void fun(n)//这里不确定参数定义几个,定义什么类型
{
	if(n==9)
	{
		
	}
}
int main()
{
	fun(1);
	return 0;
}

3.函数中的枚举部分

本题中,我们以每一列进行枚举,每一列有8个位置,所以写循环,从1枚举到8.
代码实现如下

#include<iostream>
using namespace std;

void fun(n)//这里不确定参数定义几个,定义什么类型
{
	int i;
	if(n==9)//当执行到第九层时,将不再枚举,会输出枚举结果
	{
		
	}
	for(i=1;i<=8;i++)//枚举8个位置
	{
		
	}
}
int main()
{
	fun(1);
	return 0;
}

4.枚举结果的存储与行,斜行的去重

我们可以在每次枚举时,先判断这个位置能不能放棋子,在进行下一层枚举。
我们还需要将结果存储下来,以便在递归最后一层输出。
存储结果可以用二维数组实现。
为了方便,我们将左上至右下的斜列和与之平行的斜列称之为“斜行”,将右上至左下的斜列和与之平行的斜列称之为“斜列”。
对于每一行的去重,我们定义数组f,f[m]代表第m行是否被放置过棋子,对于斜行和斜列,我们则需要通过其坐标行和列寻找关系以对其进行编号。
一个棋盘中每一个空位的坐标

通过观察,可以发现每一斜行中所有的坐标用行减去列,差都是相同的,这样我们用行列之差可知该空格属于哪一斜行,这样所得的斜行编号为-7~7。
为了使每一个编号都是大于等于1的整数,所以将每一个编号加上5,这样斜行的编号就是1~15。
而所有斜列中,每一斜列的坐标中行列之和都相等,这样得到15个斜列的编号是1~15。
所以在确定一个位置所在的行,斜行,斜列均没有放置过棋子之后,就可以记录这个位置放置棋子,并标记所在行,斜行,斜列。
注意输出时需要输出结果的编号,所以建立变量sum以记录结果的编号。

代码实现如下

#include<iostream>
using namespace std;
int sum=1,a[20][20],f[20],x[20],y[20];//f为行的标记数组,x为斜行的标记数组,y为斜列的标记数组,a为记录数组
void fun(int n)
{
	int i,j,k;
	if(n==9)
	{
		
	}
	for(i=1;i<=8;i++)
	{
		if(f[i]==0&&x[i-n+8]==0&&y[i+n]==0)//判断其所在行,斜行,斜列是否放置过棋子
		{
			f[i]=1;
			x[i-n+8]=1;
			y[i+n]=1;//标记其所在斜行,斜列,行放置了棋子
			a[i][n]=1;//记录该位置放置了棋子
			fun(n+1);//进入下一层枚举,层数n加1
			f[i]=0;
			x[i-n+8]=0;
			y[i+n]=0;
			a[i][n]=0;//该结果枚举完后,为了下一个位置的枚举,把记录数组以及标记数组清零
		}
	}
}
int main()
{
	fun(1);
	return 0;
}

4.枚举结果的存储与行,斜行的去重

当n==9时,我们输出枚举结果,注意输出格式

代码实现如下

#include<iostream>
using namespace std;
int sum=1,a[20][20],f[20],x[20],y[20];//f为行的标记数组,x为斜行的标记数组,y为斜列的标记数组,a为记录数组
void fun(int n)
{
	int i,j,k;
	if(n==9)
	{
		cout<<"No. "<<sum<<endl;//输出编号
		sum++;//编号加1,方便下一次输出结果是输出编号
		for(i=1;i<=8;i++)
		{
			for(j=1;j<=8;j++)
			{
				cout<<a[i][j]<<" ";
			}
			cout<<endl;
		}
		return ;//返回上一层循环,继续枚举
	}
	for(i=1;i<=8;i++)
	{
		if(f[i]==0&&x[i-n+8]==0&&y[i+n]==0)//判断其所在行,斜行,斜列是否放置过棋子
		{
			f[i]=1;
			x[i-n+8]=1;
			y[i+n]=1;//标记其所在斜行,斜列,行放置了棋子
			a[i][n]=1;//记录该位置放置了棋子
			fun(n+1);//进入下一层枚举,层数n加1
			f[i]=0;
			x[i-n+8]=0;
			y[i+n]=0;
			a[i][n]=0;//该结果枚举完后,为了下一个位置的枚举,把记录数组以及标记数组清零
		}
	}
}
int main()
{
	fun(1);
	return 0;
}

AC代码

#include<iostream>
using namespace std;
int sum=1,a[20][20],f[20],x[20],y[20];
void fun(int n)
{
	int i,j,k;
	if(n==9)
	{
		cout<<"No. "<<sum<<endl;
		sum++;
		for(i=1;i<=8;i++)
		{
			for(j=1;j<=8;j++)
			{
				cout<<a[i][j]<<" ";
			}
			cout<<endl;
		}
		return ;
	}
	for(i=1;i<=8;i++)
	{
		if(f[i]==0&&x[i-n+8]==0&&y[i+n]==0)
		{
			f[i]=1;
			x[i-n+8]=1;
			y[i+n]=1;
			a[i][n]=1;
			fun(n+1);
			f[i]=0;
			x[i-n+8]=0;
			y[i+n]=0;
			a[i][n]=0;
		}
	}
}
int main()
{
	fun(1);
	return 0;
}

题目来源

OpenJudge-2.5基本算法之搜索-1700八皇后问题

  • 31
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值