基础练习 2n皇后问题
【练习时间】2020/3/6
【题目名称】基础练习 2n皇后问题
资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
给定一个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
样例输入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
0
【解题思路】
通过对网上的解题思路的研究,我们选择下面这一种便于理解的解法,参考博客:[蓝桥杯][基础练习VIP]2n皇后问题-题解(C++代码) //上面大佬的翻译版
-
大致思路如下:
采用二维数组模拟棋盘,多个一维数组用于记录列和对角线棋子位置情况。
我们采用穷举的方法,逐行分别对白棋和黑棋的位置进行模拟放置,同时记录该列和对角线的情况(注意:因为我们采用的是逐行的思路,所以不用考虑相同颜色的棋子在同一行的情况),满足条件则安排下一行的位置;将上述的算法封装成函数,形参为开始遍历的行数,形参+1再次调用(递归的方法) -
详细实现过程:
初始化全局变量:
int n,ans,pan[8][8];//棋盘初始化,结果ans初始化为0
int col_w[18];//一列中白棋的位置
int col_b[18];//一列中黑棋的位置
int val_w_rd[16];//从左上到右下对角线上白棋的位置
int val_b_rd[16];//从左上到右下对角线上黑棋的位置
int val_w_ld[16];//从右上到左下对角线白棋的位置
int val_b_ld[16];//从右上到左下对角线黑棋的位置
设计函数:
void Queen(int row)//逐行放置黑白棋,从第零行开始摆放
{
if(row==n)//当摆放到第n行时,说明前n行已经摆放完毕,记录此方法 ans++
{
ans++;
return;
}
for(int i=0;i!=n;++i)//优先摆放白棋
{
if(pan[row][i]&&!col_w[i]&&!val_w_ld[row+i]&&!val_w_rd[row-i+n-1])//列、对角线都无白棋
{
pan[row][i]=0;col_w[i]=1;val_w_ld[row+i]=1;val_w_rd[row-i+n-1]=1;
for(int j=0;j!=n;++j)//摆放黑棋
{
if(pan[row][j]&&!col_b[j]&&!val_b_ld[row+j]&&!val_b_rd[row-j+n-1])//列、对角线都无黑棋
{
pan[row][j]=0;col_b[j]=1;val_b_ld[row+j]=1;val_b_rd[row-j+n-1]=1;
Queen(row+1);
//黑棋复盘
pan[row][j]=1;col_b[j]=0;val_b_ld[row+j]=0;val_b_rd[row-j+n-1]=0;
}
}
//白棋复盘
pan[row][i]=1;col_w[i]=0;val_w_ld[row+i]=0;val_w_rd[row-i+n-1]=0;
}
}
}
注意:对于如何采用一维数组记录对角线的情况我们是这样做的:
通过上图举例我们发现:
左对角线所有元素纵横坐标和都等于n。
右对角线所有元素的横坐标-纵坐标+元素个数都相同。
【源代码】
#include <iostream>
#include<algorithm>
#include <cstdio>
using namespace std;
int n,ans,pan[8][8];//棋盘初始化,结果ans初始化为0
int col_w[18];//一列中白棋的位置
int col_b[18];//一列中黑棋的位置
int val_w_rd[16];//从左上到右下对角线上白棋的位置
int val_b_rd[16];//从左上到右下对角线上黑棋的位置
int val_w_ld[16];//从右上到左下对角线白棋的位置
int val_b_ld[16];//从右上到左下对角线黑棋的位置
void Queen(int row);
int main()
{
cin>>n;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
cin>>pan[i][j];
Queen(0);
cout<<ans;
return 0;
}
void Queen(int row)//逐行放置黑白棋,从第零行开始摆放
{
if(row==n)//当摆放到第n行时,说明前n行已经摆放完毕,记录此方法 ans++
{
ans++;
return;
}
for(int i=0;i!=n;++i)//优先摆放白棋
{
if(pan[row][i]&&!col_w[i]&&!val_w_ld[row+i]&&!val_w_rd[row-i+n-1])//列、对角线都无白棋
{
pan[row][i]=0;col_w[i]=1;val_w_ld[row+i]=1;val_w_rd[row-i+n-1]=1;
for(int j=0;j!=n;++j)//摆放黑棋
{
if(pan[row][j]&&!col_b[j]&&!val_b_ld[row+j]&&!val_b_rd[row-j+n-1])//列、对角线都无黑棋
{
pan[row][j]=0;col_b[j]=1;val_b_ld[row+j]=1;val_b_rd[row-j+n-1]=1;
Queen(row+1);
//黑棋复盘
pan[row][j]=1;col_b[j]=0;val_b_ld[row+j]=0;val_b_rd[row-j+n-1]=0;
}
}
//白棋复盘
pan[row][i]=1;col_w[i]=0;val_w_ld[row+i]=0;val_w_rd[row-i+n-1]=0;
}
}
}