NOI八皇后问题
[八皇后问题链接]
(http://noi.openjudge.cn/ch0205/1700/)
从郭炜老师视频中学习了N皇后问题之后再转来写八皇后问题,舒服是舒服一点,但是理解N皇后的原理对我来说还是不太容易。从N皇后转变到八皇后只要改动一点地方,下面将是八皇后问题的理解,主要方法用递归的方式来代替多重循环。这题有非常需要注意的一点,也是我WA了很多次的一点就是输出顺序,这题的要求不仅仅是把所有的案例输出,还要按照样例里面的顺序输出,若用递归,应该是假设已经摆好前一列,再去摆后面的列,但本人写的时候用的是行而不是列,所以在最后输出皇后阵的时候将行列进行了调换,请注意。
#include<iostream>//做此题前一定明白一点,八个皇后一定在不同的行和列,所以每行一定会有一个皇后。
#include<cstdio>
#include<cmath>
#include<math.h>
int q[9];//用一个数组来记录下皇后的位置,数组的下标代表行数,数组的值代表列数。
using namespace std;
int p=0;
void queen(int k)//递归函数,用递归的思想是假设要摆的那一行的前面全都已经摆好,再摆这一行,倘若能摆下,那么用递归进入下一行的摆放。
{
if(k==8)//递归的出口,这是递归很必要的东西。当n为8的时候,说明所有皇后都被安排在合适的位置了。
{
p++;//用于计算可以摆放的案例的个数,也是序数。
cout<<"No. "<<p<<endl;
for(int j=0;j<8;++j)
{
for(int i=0;i<8;++i)
{
if(j==q[i]&&i<7)
cout<<"1"<<' ';
else if(j!=q[i]&&i<7)
cout<<"0"<<' ';
else if(j==q[i]&&i==7)
cout<<"1";
else if(j!=q[i]&&i==7)
cout<<"0";
}
cout<<endl;
}
return ;
}
for(int i=0;i<8;++i)//本题的灵魂循环。此处的i代表列数。我想可能有部分朋友刚刚学习递归会有跟我当时一样的疑问,写一个这样的循环怎么会可以判断到所有的情况呢?那其实可以列举一下两种种情况。第一种:假如完成了八行的摆放,如何进行下一次?显然,完成之后会立即return到上一次调用的时候,也就是queen(k+1)的时候,以往下走,就会让最后一行尝试下一个位置,倘若没有,则会通过正常的函数括号的出口出去再回到上一次调用的地方尝试上一个的其他位置,以此类推,会尝试所有情况。那么第二种情况其实有一部分就包含在刚才说的成功的情况当中:如果发现此行已经尝试了,所有列都不行那么也会通过函数的花括号自然出口返回到上一行,进行其他位置的尝试。那么如果所有位置都走过了,都返回到第一行的最后一列了,也就是第一行的位置都走到最后一列了,这个时候再返回就返回到main函数去了,这个时候所有的情况就输出了。总结一下,因为这个循环将0到7行的所有列的情况都遍历了,从第0行为起点开始递归,那么递归中不管在哪返回,都会进入在这个循环中尝试别的位置,所以它会尝试所有的情况。
{
int j;
for(j=0;j<k;++j)//用j去检查前面每一行,看这一行是否可以放在第i位上。
{
if(q[j]==i||abs(q[j]-i)==abs(j-k))//只要前面的皇后的列号与此行放置的列号相等,或者两者横坐标分别相减与纵坐标分别相减的绝对值相等,那么皇后的位置就不行,因为强大的皇后可以吃掉对方。
break;
}
if(j==k)//如果不是break出来的,那么j必定等于K,反之必定不等于,所以若不是break出来的代表此位置可行。
{
q[k]=i;//如果成功自然就要记录下当前的列号。
queen(k+1);//成功就去摆放下一列。
}
}
}
int main()
{
queen(0);//以第0行为起点进行尝试
}