总体思路
以列为例,每一列从第一行遍历一遍,没有危险的地方就可以放一个皇后。这个问题涉及到的小问题有几个:
1、如何判断是否安全
2、如果不安全,如何回溯
3、如何输出之后重新来一遍
整体细节
问题一
递归函数应该怎么写?
思路是,从第一列开始每一个位置遍历,然后遇到可以放皇后的位置就移向下一列,说明我们需要向函数传递表示列的参数,并且在函数内部需要一个循环来遍历8行的位置;而且调用函数的语句显然是放在循环内部,即在某一行放了一个皇后之后就进行下一列。
如何判断这个位置是否安全?
如果遍历这个位置的行与列,复杂度将会变高(每一行、每一列、两条斜线都要遍历一遍),那么可以定义四个数组,分别代表行,列,两条斜线,每个数组的里面的数代表这一行(列、斜线)是否安全,如,用1表示安全,用0表示不安全,那么初始化为1,然后放好一个皇后之后要更新它所在的行列与斜线
问题二
需要回溯的情况
如果所在的行列与斜线都不安全,而且这一列没有地方可以放皇后,就需要回溯。回溯的结果是回到上一列,将刚才放置皇后的位置初始化,然后换下一个安全的位置放皇后。
回溯的语句放在哪里?
根据回溯的条件,我们需要在循环到最后一行也不能放皇后的时候回溯,因此,我们在循环行完毕之后进行回溯。
回溯要做些什么?
将棋盘和行数组元素、列数组元素、两条斜线数组元素初始化。然后需要返回上一个被调用的函数。
问题三
如何输出
可以在定义一个函数用来输出。注意输出格式与空格。
void print()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (j < 7)//非最后一个格子
{
if (board[i][j] == 0)
cout << "." << " ";//没有皇后
else
cout << "*" << " ";//放置皇后
}
else//最后一个格子
{
if (board[i][j] == 0)
cout << ".";
else
cout << "*";
}
}
cout << endl;
}
}//输出表格
在哪里输出?
如果每一列都有皇后了,即函数的参数已经到最后一列并且放好了皇后,那么就可以输出。
在函数的一开始判断传递进来的参数的大小,如果是最后一列已经放好了,就可以输出答案标号和棋盘了。
输出之后如何进行下一步的探索?
输出后回到上一个被调用的函数,然后就可以正常进行接下来的遍历了。
递归函数片段(有一个地方还没有想到比较好的解决办法)
void f(int column)
{
if (p <= 92)//就是这个地方
if (column < 8)
{
for (int i = 0; i < 8; i++)
{
if (!row[i] && !col[column] && !Left[i+column] && !Right[7+i-column])//如果这个点是安全的,就可以放
{
row[i] = 1;
col[column] = 1;
Left[i+column] = 1;
Right[7+i-column] = 1;
board[i][column] = 1;//更新数据
f(column+1);
board[i][column] = 0;
row[i] = 0;
col[column] = 0;
Left[i+column] = 0;
Right[7+i-column] = 0;//回溯
}
}
}
else
{
cout << p << endl;
print();
p++;//输出标号
}
}
由于八皇后问题有92解,然后我暂时没有想到可以在9解之后结束输出的办法,就只好用p<=92来结束。实际上,如果没有这个条件控制的话,那么程序会输出第一列没有放皇后的棋盘,第二列没有放皇后的棋盘……
关于整个代码
没有什么好说的,要注意的问题也就是先定义输出棋盘的函数,因为在递归函数里面要调用输出函数;然后主要的变量都是定义成全局变量。主函数如下:
int main()
{
for (int k = 0; k < 8; k++)
{
f(k);//将列参数传递进去
}
return 0;
}
写在最后
这是智障博主的第一篇博客,然后也不是很专业。毕竟智障博主才学c++不到三个月,写这个只是为了智障博主以后复习c++。也请大家不要嫌弃,然后有优秀的小伙伴可以帮忙想一下那个问题呗,给大家坐下了。