蓝桥杯学习记录
一、测试练习:
问题名称:2n皇后问题
问题描述:
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入格式:
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
输出格式:
输出一个整数,表示总共有多少种放法。
样例输入:
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出:
2
解题思路:
本题目刚读完题目描述的时候并没有什么好的解决办法,所以一开始用暴力解法,逻辑结构复杂,代码量长,并且也没有解出来。本题的巧妙解法是利用递归,和设置一个pos[]数组,(pos[i] = j 表示在i列j行放置一枚棋子) 这样设置数组最重要的体现是在判断是否能放棋子的函数上,避免了递归回溯重置数组的难题,即如果不这样表示,那么pos数组作为全局变量每次递归都会调用这个数组来查看已经放置的棋子,那么就会产生一个问题——如果一种放置棋子的方式失败,那么这种放置棋子的方式就必须全部清零,如果不清零会出现很多错误。使用了这个数组,就可以按列遍历这个数组,只看当前列以前放置的棋子,根本不用在乎其他列上是否清零。
本题的具体做法是按列遍历给的棋盘,用check函数判断一下是否能放棋子,如果能放棋子就遍历下一列,先放黑皇后再放白皇后,白皇后放完后sum++,最后输出sum 的数值就可以。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
int mapQ[9][9];
int sum = 0;
int cur;
int posB[9] = {0};//赋初值为0
int posW[9] = {0};//赋初值为0
bool checkW(int l, int c, int n)//检查是否能放置白皇后的函数
{
for(int i = 1; i < c; i++)//遍历到当前列的前一列即停止,因为就放了c-1个棋子
if(posW[i] == l || abs(l - posW[i]) == abs(c - i))//因为不可能再同一列所以只需要判断是否在同一行和同一对角线即可
return 0;
return 1;
}
bool checkB(int l, int c, int n)//检查是否能放置黑皇后的函数
{
for(int i = 1; i < c; i++)//遍历到当前列的前一列即停止,因为就放了c-1个棋子
if(posB[i] == l || abs(l - posB[i]) == abs(c - i)))//因为不可能再同一列所以只需要判断是否在同一行和同一对角线即可
return 0;
return 1;
}
void putW(int n, int cur)//放置白皇后的函数
{
if(cur == n+1)
sum++;
for(int i = 1; i <= n; i++)
{
if(mapQ[i][cur] != 0 && posB[cur] != i && checkW(i,cur,n))//判断一下是否能放棋子,是否已经放置了黑皇后
{ //是否可以放白皇后
posW[cur] = i;
putW(n,cur+1);//递归在下一列放棋子
}
}
}
void putB(int n, int cur)//放置黑皇后的函数
{
if(cur == n+1)
putW(n,1);//放完黑皇后就放白皇后
for (int i = 1; i <= n; i++)
{
if(mapQ[i][cur] != 0 && checkB(i,cur,n))//判断一下是否能放棋子,是否可以放置黑皇后
{
posB[cur] = i;
putB(n,cur+1);//递归在下一列放棋子
}
}
}
int main()
{
int n;
cin>>n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
cin>>mapQ[i][j];
putB(n,1);
cout<<sum;
}