八皇后问题
题目描述
在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。
输入格式
无输入
输出格式
按给定顺序和格式输出所有八皇后问题的解(见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;
}